Lists and hashes
When you work with formulas and repeating structures, there are 2 key data structures you need to understand: arrays (lists) and hashes. Take note that most formulas will return an error and stop the job if it tries to operate on nulls (expressed as nil in Ruby), except for present?, presence and blank?.
Lists (arrays)
Arrays are ordered, integer-indexed collections of any object. List indexing starts at 0. Lists are the same as Ruby arrays, and we will be using lists and arrays interchangeably in this article.
Let's take the example of a list with 4 list items: 100, 101, 102, 103. This list is expressed as:
1number_list = [100, 101, 102, 103, 104]2
As lists are ordered, we can use the following formula to get the values. AppConnect only supports retrieving up to the fifth item in the list:
Formula | Result |
---|---|
number_list.first | 100 |
number_list.second | 101 |
number_list.third | 102 |
number_list.fourth | 103 |
number_list.fifth | 104 |
number_list.last | 104 |
We can also use indexes to get corresponding values. Remember, indexes start at 0:
Formula | Result |
---|---|
number_list[0] | 100 |
number_list[1] | 101 |
number_list[2] | 102 |
number_list[3] | 103 |
Lists in Ruby supports negative indexes.
Formula | Result |
---|---|
number_list[-1] | 104 |
number_list[-2] | 103 |
number_list[-3] | 102 |
number_list[-4] | 101 |
Lists also support ranges as indexes. This returns another list, instead of returning only a value.
Formula | Result |
---|---|
number_list[0..2] | [100, 101, 102] |
number_list[-3..-1] | [102, 103, 104] |
number_list[0..-2] | [100, 101, 102, 103] |
Hashes
A hash is a dictionary-like collection of unique keys and their values. They are similar to Lists, but where a List uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.
Let's take the example of a hash with 2 values, with 'Acme widgets' and 10 as the values of item_name and item_quantity respectively.
1line_item = { 'item_name' => 'Acme widgets', 'item_qty' => 10 }2
Formula | Result |
---|---|
line_item["item_name"] | "Acme widgets" |
line_item["item_qty"] | 10 |
List of hashes
In AppConnect, you will mostly run into lists of hashes. Let's look at a Quickbooks invoice which has a number of line items. It will be represented as an list of hashes.
1line_items = [ # list2 { 'item_name' => 'Acme widgets', 'item_qty' => 10 }, # hash 13 { 'item_name' => 'RR bearings', 'item_qty' => 100 }, # hash 24 { 'item_name' => 'Coyote tyres', 'item_qty' => 7 } # hash 35]6
Formulas
AppConnect supports a variety of list formulas. Formulas in AppConnect are whitelisted Ruby methods, and therefore not all Ruby methods are supported. You can always reach out to us to add additional formulas to the whitelist. Syntax and functionality for these formulas are generally unchanged.
Example list of hashes
The following is an example of a list of hashes called Contacts.
This is the Contacts list in a table form:
name | state | company | company_rev | |
---|---|---|---|---|
Joe | CA | ABC | 1000 | |
Jill | MA | NBC | 1000 | |
Joan | MA | NBC | 10000 | |
Jack | CA | HBO | 30000 |
This is the Contacts list in a list of hashes form.
1[2 {3 'name' => 'Joe',4 'email' => 'joe@abc.com',5 'state' => 'CA',6 'company' => 'ABC',7 'company_rev' => 1000,8 'description' => { 'summary' => 'First time buyer', 'estimated_value' => 300 }9 },10 {11 'name' => 'Jill',12 'email' => 'jill@nbc.com',13 'state' => 'MA',14 'company' => 'NBC',15 'company_rev' => 1000,16 'description' => { 'summary' => 'Referral', 'estimated_value' => 500 }17 },18 {19 'name' => 'Joan',20 'email' => 'joan@nbc.com',21 'state' => 'MA',22 'company' => 'NBC',23 'company_rev' => 10000,24 'description' => { 'summary' => 'Recurring customer', 'estimated_value' => 900 }25 },26 {27 'name' => 'Jack',28 'email' => 'jack@hbo.com',29 'state' => 'CA',30 'company' => 'HBO',31 'company_rev' => 30000,32 'description' => { 'summary' => 'Recurring customer', 'estimated_value' => 1000 }33 }34]35
first
This formula returns the first item in a list.
It can also be used to return the first n items in a list. In this case, the output will be formatted as a list.
Syntax
List.first(number)
List - An input list.
number - (optional) The number of items to retrieve from the list. If not specified, the formula will return only one item.
Sample usage
Formula | Result |
---|---|
["One","Two","Three","Four","Five"].first() | "One" |
["One","Two","Three","Four","Five"].first(2) | ["One","Two"] |
[1,2,3,4,5].first() | 1 |
[1,2,3,4,5].first(3) | [1,2,3] |
How it works
This formula returns the first n items from a list. If n is greater than one, the output is formatted as a list.
If you are returning a single item (i.e. no arguments provided). The output will be formatted according to the item's datatype.
If you are returning more than one item. The output will be formatted as a list datatype.
last
This formula returns the last item in a list.
It can also be used to return the last n items in a list. In this case, the output will be formatted as a list.
Syntax
List.last(number)
List - An input list.
number - (optional) The number of items to retrieve from the list. If not specified, the formula will return only one item.
Sample usage
Formula | Result |
---|---|
["One","Two","Three","Four","Five"].last() | "Five" |
["One","Two","Three","Four","Five"].last(2) | ["Four","Five"] |
[1,2,3,4,5].last() | 5 |
[1,2,3,4,5].last(3) | [3,4,5] |
How it works
This formula returns the last n items from a list. If n is greater than one, the output is formatted as a list.
If you are returning a single item (i.e. no arguments provided). The output will be formatted according to the item's datatype.
If you are returning more than one item. The output will be formatted as a list datatype.
index
Returns the index of the first item matching the given value.
Syntax
Input.index(value)
Input - An input list.
value - The value to search for in the list.
Sample usage
Formula | Result |
---|---|
[4, 5, 6, 7].index(6) | 2 |
[4, 5, 6, 7].index(8) | nil |
where
Retrieves only the rows (hashes) which satisfy the specified WHERE condition. This formula accepts a single argument in the form of a hash with one or more key-value pairs.
The default operand for the condition is equal to (==). This formula also supports the following operands. Operands should be added to the end of key separated by a space.
Name | Notation | Example |
---|---|---|
Equal to (default) | == | leads.where('state': 'CA') |
More than | > | leads.where('company_revenue >": 10000) |
More than or equal to | >= | leads.where('company_revenue >=": 10000) |
Less than | < | leads.where('company_revenue <": 10000) |
Less than or equal to | <= | leads.where('company_revenue <=": 10000) |
Not equal to | != | leads.where('state !=': 'CA') |
Use datapills as the conditional argument
Instead of using a static value (e.g. 'CA'), you can use a datapill as the conditional argument. The value of the datapill will be processed at run-time.
contacts.where(state: datapill )
Sample usage
Example of a single where conditionExample of compound where formulaExample of multiple matchesExample where condition with pattern matchingExample where condition with pattern matching (using datapills)Example of chaining where conditions
except
Returns a hash that includes everything except given keys.
1hash = { a: true, b: false, c: nil }2hash.except(:c) # => { a: true, b: false }3hash.except(:a, :b) # => { c: nil }4hash # => { a: true, b: false, c: nil }5
pluck
Retrieves only the columns which have been specified.
Sample usage
Example of a single column dataputExample of a multiple column datasetExample of retrieving nested fields
format_map
Create an array of strings by formatting each row of given array of hashes. Allows you to add static text to the created strings as well. Fields to be represented in the format %{<field_name>}.
Sample usage
contacts.format_map('Name: %{name}, Email: %{email}, Company: %{company}') returns
1[2 'Name: Joe, Email: joe@abc.com, Company: ABC' ,3 'Name: Jill, Email: jill@nbc.com, Company: NBC' ,4 'Name: Joan, Email: joan@nbc.com, Company: NBC' ,5 'Name: Jack, Email: jack@hbo.com, Company: HBO' ,6]7
The above example will give you a list of strings, one string for each row of the list "contacts", using data from 3 of the fields: name, email and company, as stated.
join
Combines all items in a list into a text string. A separator is placed between each item.
Syntax
List.join(separator)
List - An input of list datatype.
separator - The character to add between items when they are joined. If no separator is specified, the list items will be joined together.
Sample usage
Formula | Result |
---|---|
["Ms", "Jean", "Marie"].join("-") | "Ms-Jean-Marie" |
[1,2,3].join("--") | "1--2--3" |
["ab", "cd", "ef"].join | "abcdef" |
How it works
The list items are combined into a single text string. The seperator character(s) is added between each item.
Seperator character
You can use a string of characters together as the seperator argument (e.g. ", ").
["Open","Pending","Closed"].split(", ") returns "Open, Pending, Closed".
smart_join
Joins list elements into a string. Removes empty and nil values and trims any white space before joining.
Syntax
List.smart_join(separator)
List - An input of list datatype.
separator - The character to add between items when they are joined. If no separator is specified, a blank space will be used as the joining character.
Sample usage
Formula | Result |
---|---|
[nil, "", "Hello", " ", "World"].smart_join(" ") | "Hello World" |
["111 Vinewood Drive", "", "San Francisco", "CA", "95050"].smart_join(",") | "111 Vinewood Drive, San Francisco, CA, 95050" |
reverse
Reverses the order of a list.
Syntax
List.reverse
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
["Joe", "Jill", "Joan", "Jack"].reverse | ["Jack", "Joan", "Jill", "Joe"] |
[100, 101, 102, 103].reverse | [103, 102, 101, 100] |
sum
For integers and decimals, the numbers will be added together and the total sum obtained. For strings, the strings will be concatenated together to form a longer string.
Syntax
List.sum
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
[1, 2, 3].sum | 6 |
[1.5, 2.5, 3].sum | 7.0 |
["abc", "xyz"].sum | "abcxyz" |
uniq
Returns a list containing unique items i.e. remove duplicate items.
Syntax
List.uniq
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
["joe", "jack", "jill", "joe", "jack"].uniq | ["joe","jack", "jill"] |
[1, 2, 3, 1, 1, 3].uniq | [1, 2, 3] |
[1.0, 1.5, 1.0].uniq | [1.0, 1.5] |
flatten
Flattens a multi-dimensional array (i.e. array of arrays) to a single dimension array.
Syntax
List.flatten
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
[[1, 2, 3], [4, 5, 6]].flatten | [1, 2, 3, 4, 5, 6] |
[[1, [2, 3], 3], [4, 5, 6]].flatten | [1, 2, 3, 3, 4, 5, 6] |
[[1, [2, 3], 9], [9, 8, 7]].flatten | [1, 2, 3, 9, 9, 8, 7] |
length
Returns the number of elements in self. Returns 0 if the list is empty.
Syntax
List.length
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
[ 1, 2, 3, 4, 5 ].length | 5 |
[{..}, {..}, {..}].length | 3 |
[" ", nil, "", nil].length | 4 |
[].length | 0 |
max
Returns largest value in an array. When comparing numbers, the largest number is returned. When comparing strings, the string with the largest ASCII value is returned.
Syntax
List.max
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
[-5, 0, 1, 2, 3, 4, 5].max | 5 |
[-1.5, 1.5, 2, 3, 3.5].max | 3.5 |
["cat", "dog", "rat"].max | "rat" |
min
Returns smallest value in an array. When comparing numbers, the smallest number is returned. When comparing strings, the string with the smallest ASCII value is returned.
Syntax
List.min
List - An input of list datatype.
Sample usage
Formula | Result |
---|---|
[-5, 0, 1, 2, 3, 4, 5].min | -5 |
[-1.5, 1.5, 2, 3, 3.5].min | -1.5 |
["cat", "dog", "rat"].min | "cat" |
compact
Removes nil values from array and hash.
Sample usage
Formula | Result |
---|---|
["foo", nil, "bar"].compact | ["foo", "bar"] |
{ foo: 1, bar: nil, baz: 2 }.compact | { foo: 1, baz: 2 } |
Conditionals
blank?
This formula checks the input string and returns true if it is an empty string or if it is null.
Syntax
Input.blank?
Input - An input datapill. It can be a string, number, date, or datetime datatype.
Sample usage
Formula | Result |
---|---|
"Any Value".blank? | false |
123.blank? | false |
0.blank? | false |
"".blank? | true |
How it works
If the input is null or an empty string, the formula will return false. For any other data, it returns true.
include?
Checks if the string contains a specific substring. Returns true if it does.
Syntax
Input.include?(substring)
Input - A string input.
substring - The substring to check for.
Sample usage
Formula | Result |
---|---|
"Partner account".include?("Partner") | true |
"Partner account".include?("partner") | false |
How it works
This formula check is the string contains a specific substring. Returns true if it does, otherwise, returns false. This substring is case sensitive.
This function acts in an opposite manner from exclude?. It will return true only if the input string contains the stated keyword.
present?
This formula will check the input and if there is a value present, it will return true. If the input is nil, an empty string or an empty list, the function will return false.
Syntax
Input.present?
Input - An input datapill. It can be a string, number, date, or list datatype.
Sample usage
Formula | Result |
---|---|
"Any Value".present? | true |
123.present? | true |
0.present? | true |
"2017-04-02T12:30:00.000000-07:00".present? | true |
nil.present? | false |
"".present? | false |
[].present? | false |
How it works
If the input is null, an empty string or an empty list, the formula will return false. For any other data, it returns true.
Evaluating a list with nil values
Only an empty list will return false.
[].present? returns false.
A list with nil and empty string will return true.
[nil,""].present? returns true.
presence
Returns the data if it exists, returns nil if it does not.
Syntax
Input.presence
Input - An input datapill. It can be a string, number, date, or datetime datatype.
Sample usage
Formula | Result |
---|---|
nil.presence | nil |
"".presence | nil |
"Any Value".presence | "Any Value" |
45.0.presence | 45.0 |
0.presence | 0 |
How it works
If the input is null or an empty string, the formula will return nil. For any other data, it returns the orignal input data.
Conversion
The following formulas allow you to convert data from arrays to other data types
to_csv
Generates CSV line from an array. This handles escaping. Nil values and empty strings will also be expressed within the csv line.
Syntax
Input.to_csv
Input - An input of list datatype.
Sample usage
Formula | Result |
---|---|
["John Smith", "No-Email", " ", nil, "555-1212"].to_csv | "John Smith,No-Email, ,,555-1212" |
["John Smith", "No-Email", " ", nil, 1212].to_csv | "John Smith,No-Email, ,,1212" |
to_json
Converts hash or array to JSON string.
Syntax
Input.to_json
Input - An input datapill. It can be a list or hash datatype.
Sample usage
Formula | Result |
---|---|
{"pet" => "cat", "color" => "gray"}.to_json | {"pet":"cat","color":"gray"} |
["1","2","3"].to_json | ["1", "2", "3"] |
to_xml
Converts hash or array into XML string.
Syntax
Input.to_xml
Input - An input datapill. It can be a list or hash datatype.
Sample usage
Formula | Result |
---|---|
{"name" => "Ken"}.to_xml(root: "user") | <user><name>Ken</name></user> |
[{"name" => "Ken"}].to_xml(root: "users") | <users><user><name>Ken</name></user></users> |
from_xml
Converts XML string to hash.
Syntax
Input.from_xml
Input - Input XML data.
Sample usage
Converting XML string to hash
encode_www_form
Join hash into url-encoded string of parameters.
Syntax
Input.encode_www_form
Input - An input of hash datatype.
Sample usage
Formula | Result |
---|---|
{"apple" => "red green", "2" => "3"}.encode_www_form | "apple=red+green&2=3" |
to_param
Returns a string representation for use as a URL query string.
Syntax
Input.to_param
Input - An input of hash datatype.
Sample usage
Formula | Result |
---|---|
{name: 'Jake', age: '22'}.to_param | "name=Jake&age=22" |
keys
Returns an array of keys from the input hash.
Syntax
Input.keys
Input - An input of hash datatype.
Sample usage
Formula | Result |
---|---|
{"name" => 'Jake', "age" => '22'}.keys | ["name", "age"] |
Other formulas
This section covers formulas that work with a number of data types.
null
Gives a null/nil value. Note: passing this into an input field will not update the field value as null. Use clear formula to update a field value to null. Remember to toggle the field to formula mode.
clear
Clears the value of the field in the target app to null/nil. Remember to toggle the field to formula mode.
Use clear formula instead of null when looking to clear field in target app
uuid
Generates an UUID.
Example
Example | Result |
---|---|
uuid | "c52d735a-aee4-4d44-ba1e-bcfa3734f553" |
encrypt
Encrypts the input string with a secret key using AES-256-CBC algorithm. Encrypted output string is packed in RNCryptor V3 (opens new window)format and base64 encoded.
Note: The encryption key should not be hard coded in the recipe. Use account properties (with key or password in the name) to store the encryption keys.
Example
encrypt([ssn], [encryption_key])
decrypt
Decrypts the encrypted input string with a secret key using AES-256-CBC algorithm. Encrypted input string should be packed in RNCryptor V3 (opens new window)format and base64 encoded.
Note: The encryption key should not be hard coded in the recipe. Use account properties (with key or password in the name) to store the encryption keys.
Example
decrypt([encrypted_ssn], [encryption_key])
encode_sha256
Encodes a string or binary array using SHA256 algorithm
Example
"hello".encode_sha256
encode_hex
Converts binary string to its hex representation
Example
Example | Result |
---|---|
"0101010101011010".encode_hex | "30313031303130313031303131303130" |
decode_hex
Decode hexadecimal into binary string
Example
Example | Result |
---|---|
"30313031303130313031303131303130".decode_hex | "0101010101011010" |
encode_base64
Encode using Base64 algorithm
Example
Example | Result |
---|---|
"Hello World!".encode_base64 | "aGVsbG8gd29ybGQh" |
decode_base64
Decode using Base64 algorithm
Example
Example | Result |
---|---|
"aGVsbG8gd29ybGQh".decode_base64 | "Hello World!" |
encode_url
URL encode a string
Example
Example | Result |
---|---|
"Hello World".encode_url | "Hello%20World" |
encode_urlsafe_base64
Encode using urlsafe modification of Base64 algorithm
Example
Example | Result |
---|---|
"Hello World".encode_urlsafe_base64 | "SGVsbG8gV29ybGQ=" |
decode_urlsafe_base64
Decode using urlsafe modification of Base64 algorithm
Example
Example | Result |
---|---|
"SGVsbG8gV29ybGQ".decode_urlsafe_base64 | "Hello World" |
as_string
Decode byte sequence as string in given encoding
Example
Example | Result |
---|---|
"SGVsbG8gV29ybGQ=".decode_base64.as_string('utf-8') | "Hello World" |
as_utf8
Decode byte sequence as UTF-8 string
Example
Example | Result |
---|---|
"SGVsbG8gV29ybGQ=".decode_base64.as_utf8 | "Hello World" |
to_hex
Converts binary string to its hex representation
Example
Example | Result |
---|---|
"SGVsbG8gV29ybGQ=".decode_base64.to_hex | "48656c6c6f20576f726c64" |
SHA1
Encrypts a given string using the SHA1 encryption algorithm. Details here(opens new window)
Example
Example | Result |
---|---|
"abcdef".sha1.encode_base64 | "H4rBDyPFtbwRZ72oS4M+XAV6d9I=" |
HMAC formulae
Creates a HMAC signatures with a variety of signing algorithms
Example
Signing algorithm | Example |
---|---|
SHA-256 | "username:password:nonce".hmac_sha256("key") |
SHA-1 | "username:password:nonce".hmac_sha1("key") |
SHA-512 | "username:password:nonce".hmac_sha512("key") |
MD5 | "username:password:nonce".hmac_md5("key") |
md5_hexdigest
Accepts a string and creates message digest using the MD5 Message-Digest Algorithm
Example
Example | Result |
---|---|
"hello".md5_hexdigest | "5d41402abc4b2a76b9719d911017c592" |
jwt_encode_rs256
Creates JWT with RS256 - RSA using SHA-256 hash algorithm
Example
Example | Result |
---|---|
appconnect.jwt_encode_rs256({ name: "John Doe" }, "PEM key") | "eyJhbGciO..." |
parse_yaml
Parse a YAML string. Supports true, false, nil, numbers, strings, arrays, hashes
Example
Example | Result |
---|---|
appconnect.parse_yaml("---\nfoo: bar") | "{ "foo" => "bar" }" |
appconnect.parse_yaml("---\n- 1\n- 2\n- 3\n") | "[1, 2, 3]" |
render_yaml
Render an object into a YAML string.
Example
Example | Result |
---|---|
appconnect.render_yaml({ "foo" => "bar" }) | "---\nfoo: bar\n" |
appconnect.render_yaml([1,2,3]) | "---\n- 1\n- 2\n- 3\n" |
lookup
This formula allows you to lookup values from your AppConnect lookup tables via a key. It is case sensitive and data type sensitive.
If you use a data pill in the lookup formula, it is recommended that the data is converted to the right format. For example, integer-type data pills should be converted to string with a .to_s formula if comparing to a column containing both integers and strings.
Example
For example, let's use the following lookup table with name Department Codes with an ID of 6:
Example | Result |
---|---|
lookup('Department Codes', 'Department code': 'ACC')['Department'] | "Accounting" |
lookup('Department Codes', 'Department code': 'SLS')['Department'] | "Sales" |
lookup('Department Codes', 'Department': 'Marketing')['Department code'] | "MKT" |
lookup('6', 'Department code': 'ACC')['Department'] | "Accounting" #interchangeable lookup table name and ID |
lookup('Department Codes', 'Department': 'marketing')['Department code'] | nil #case sensitive value for "Marketing" |
lookup('Department Codes', 'Department': 'Marketing')['Department Code'] | nil #case sensitive value for column name "Department code" |
lookup_table
This formula allows you to create a static lookup table and define the keys and values. It is case sensitive and data type sensitive.
Example
Example | Result |
---|---|
{"key1" => "value1", "key2" => "value2", "key3" => "value3"}["key2"] | "value2" |
{"High" => "urgent", "Medium" => "mid", "Low" => "normal"}["Low"] | "normal" |
{"High" => "urgent", "Medium" => "mid", "Low" => "normal"}["low"] | nil |
{"High" => "urgent", "Medium" => "mid", "Low" => "normal"}["normal"] | nil |
{1 => "1", 2 => "2", 3 => "3"}[2] | "2" |
{1 => "1", 2 => "2", 3 => "3"}[2.0] | nil |
{1 => "1", 2 => "2", 3 => "3"}["2"] | nil |
Complex data types
Up to this point, we have only discussed primitive data types like Strings and Integers. With most APIs, data is represented with more complex structures. These can be broadly classified into JSON Objects and Arrays.
Mapping complex data in formula mode
Benefits
Deal with primitive arrays
You may not have prior knowledge of schema
Too many fields in a single object to map
Example
In the following example, we apply tags to Zendesk tickets for associated Escalations, which (in this example) we are tracking in Insightly as a custom object. For the purpose of demonstration, we will perform this using a custom action. According to Zendesk API documentation (opens new window), tags should be sent as an array of strings. The payload should look like this:
1{2 "ticket": {3 "tags": ["WorkJam", "ProductHour"]4 }5}6
Now, this presents a problem for us, because AppConnect input field mapping exist primary as key/value pairs. This required format is a primitive array of strings. This requires a complex data type (Array of Strings).
To do this, simply toggle the Tags input field to formula mode, and form the required structure. This can be done in a few ways.
First, we can perform a test with a statically defined array of strings. Make sure that the input value observes proper JSON syntax. Invalid values in formula mode will raise design time formula errors.
Next, it is a good idea to perform some tests to make sure that the custom action updates the ticket with the new tags as expected. At this point, you can improve the recipe further to update tickets with tags dynamically.
Since Zendesk API expect an Array of Strings, we need to retrieve only the Escalation Name values from the Escalations returned from a previous action.
We do this by using the pluck formula, which conveniently returns an array of Escalation Name values, perfect!
Finally, make sure that the right data is sent by checking the job details. Always make sure to test your work before setting a recipe live!