Search

Dark theme | Light theme
Showing posts with label DataWeave. Show all posts
Showing posts with label DataWeave. Show all posts

April 7, 2023

DataWeave Delight: Using The log Function

The log function in the dw::Core module allows to log a value or an expression. The function returns the input unchanged. This means we can wrap our code with the log function and the code is still executed as is, but also logged in a system log. As an extra argument we can specify a String value that will be a prefix to the expression value in the logging output. The fact that the input is also returned makes it very easy to add the log function to our DataWeave expressions.

In the following example we use the log function for several use cases:

Source

%dw 2.0

import upper from dw::core::Strings

// Sample object we want to use for logging.
var user = {
    alias: "mrhaki",
    name: {
        firstName: "Hubert",
        lastName: "Klein Ikkink"
    }
}

output application/json
---
{
    // Log a value.
    logValue: log("DataWeave"),

    // Log expression.
    logUpper: log(upper("DataWeave")),

    // Log object property.
    logExpr: log(user.alias),

    // Log with prefix.
    logExprWithPrefix: log("alias", user.alias),

    // Log object.
    logName: log("name", user.name)
}

Output

{
  "logValue": "DataWeave",
  "logUpper": "DATAWEAVE",
  "logExpr": "mrhaki",
  "logExprWithPrefix": "mrhaki",
  "logName": {
    "firstName": "Hubert",
    "lastName": "Klein Ikkink"
  }
}

We see the output simply returns the original object that we passed to the log function. But when we look at a log viewer, for example the console of a Mule application, we see a representation of the object. And if we specified a prefix the prefix is also visible in the logging output.

The previous code returns the following log output:

"DataWeave"
"DATAWEAVE"
"mrhaki"
alias - "mrhaki"
name - { firstName: "Hubert", lastName: "Klein Ikkink" }

Written with DataWeave 2.4.

November 24, 2022

DataWeave Delight: Turn String Into Snake Case With underscore

In a previous post we learned we can turn a string into a string with kebab casing using dasherize from the dw::core::Strings module. If we want to turn a string into a string with camel casing we can use the underscore function. The underscore function will replace spaces, dashes and camel-casing with underscores, which makes the result snake-casing. Any uppercase characters are transformed to lowercase characters.

In the following example we use the underscore function with different input string arguments:

Soure

%dw 2.0
import underscore from dw::core::Strings
    
output application/json
---
{
    // Replaces camel casing with underscores.
    camelCase: underscore("stringInCamelCase"), // string_in_camel_case
    
    // Replaces dashes with underscores.
    kebab_case: underscore("string-with-dashes"), // string_with_dashes
    
    // Replaces spaces with underscores.
    spaces: underscore("string with spaces"), // string_with_spaces
    
    // Uppercase is transformed to lowercase.
    upper: underscore("STRING_WITH_UPPERCASE") // string_with_uppercase
}

Output

{
  "camelCase": "string_in_camel_case",
  "kebab_case": "string_with_dashes",
  "spaces": "string_with_spaces",
  "upper": "string_with_uppercase"
}

Written with DataWeave 2.4.

November 20, 2022

DataWeave Delight: Turn String Into Kebab Casing With dasherize

The dw::core::Strings module has useful functions for working with string values. One of the functions is dasherize. The function takes a string argument and replaces spaces, underscores and camel-casing into dashes. The resulting string value with hyphens is also called kebab-casing. The dasherize function also turns any uppercase character to lowercase.

In the following example we have different input arguments with camel-casing, underscores and spaces:

Source

%dw 2.0
import dasherize from dw::core::Strings

output application/json
---
{
    // Replaces camel casing with dashes.
    camelCase: dasherize("stringInCamelCase"), // string-in-camel-case

    // Replaces underscores with dashes.
    snake_case: dasherize("string_with_underscores"), // string-with-underscores

    // Replaces spaces with dashes.
    spaces: dasherize("string with spaces"), // string-with-spaces

    // Uppercase is transformed to lowercase.
    upper: dasherize("STRING_WITH_UPPERCASE") // string-with-uppercase
}

Output

{
  "camelCase": "string-in-camel-case",
  "snake_case": "string-with-underscores",
  "spaces": "string-with-spaces",
  "upper": "string-with-uppercase"
}

Written with DataWeave 2.4.

September 27, 2022

DataWeave Delight: Add Conditionally Element to Array or Object

The core of DataWeave is to transform data from one format to another format. If we want to include an element in an array based on a condition we can enclose the element between parenthesis (element) followed by an if statement. If the condition of the if statement returns true the element is included in the array. To include a key/value pair in an object based on a condition we also enclose the key/value pair in parenthesis (key: value) and add an if statement. When the condition specified is true the key/value pair is added to the object, otherwise it will be left out.

In the following example we add conditionally the key website and each element of the array is added based on a condition. The conditions check values in a conf object defined at the top of the example:

Source

%dw 2.0

// Helper object with values used to add elements conditionally.
var conf = {
    kind: "FP",
    includeWebsite: true
}

output application/json
---
[
    ({
        language: "DataWeave", 
        kind: "Functional",
        // Add key/value to object if condition is true.
        (website: "https://docs.mulesoft.com/dataweave/2.4/") if conf.includeWebsite
    }) 
    // Add element to array if condition is true.
    if conf.kind == "FP", 
    
    ({
        language: "Java", 
        kind: "Object Oriented",
        (website: "https://www.java.com/") if (conf.includeWebsite)
    }) if (conf.kind == "OO")
]

Result

[
    {
        "language": "DataWeave",
        "kind": "Functional",
        "website": "https://docs.mulesoft.com/dataweave/2.4/"
    }
]

When we change the values in our conf object we get a different result:

Source

var conf = {
    kind: "OO",
    includeWebsite: false
}

Result

[
    {
        "language": "Java",
        "kind": "Object Oriented"
    }
]

Written with DataWeave 2.4.

DataWeave Delight: Using the update Operator to change values in an object

DataWeave has some very nice features to transform data objects. One of those nice features is the update operator. With the update operator we can change values of keys in an object using a very concise syntax. We don't have to go through all keys and create a new object, but we can pinpoint the exact key and change the value. To get the correct key we use selectors. Once we have the key we can set a new value. We can define a variable to contain the current value if we want to use it to define a new value. Also is it possible to add a condition that needs to be true to change the value. Finally the update operator supports upserting a value if the key might not exist yet.

The complete syntax of the update operator is as follows:

<object> update {
    case <optional variable> at <key selector> <optional condition if (...)> -> <new value> 
}

In the following example we see several use cases of the update operator:

Source

%dw 2.0

var obj = {
    user: {
        firstName: "Hubert",
        lastName: "Klein Ikkink"
    },
    alias: "mrhaki",
    country: "NL"
}

output application/json
---
obj update {    
    // Simply use . selector to get key and set new value
    case .alias ->  "haki"
    
    // Using ! to upsert a key if doesn't exist yet.
    case .likes! -> ["Clojure", "DataWeave", "Groovy"]

    // We can use a variable for the selector value
    // and use it for a new value
    case firstName at .user.firstName -> firstName[0] ++ ".A."

    // DataWeave always provides $ as variable if we don't use at.
    // Here we can use $ to define a new value.
    case .user.lastName -> $ splitBy " " map ((item) -> item[0]) joinBy ""

    // We can add a condition for which we want to update the key
    // with a new value. In this example if country is "NL"
    // the value is "Netherlands", for other values of country
    // no transformation happens.
    case country at .country if (country == "NL") ->  "Netherlands" 
}    

Output

{
    "user": {
        "firstName": "H.A.",
        "lastName": "KI"
    },
    "alias": "haki",
    "country": "Netherlands",
    "likes": [
        "Clojure",
        "DataWeave",
        "Groovy"
    ]
    }    

Written with DataWeave 2.4.

July 12, 2022

DataWeave Delight: Define Multi Line Strings

To define a string value in DataWeave we can use both double quotes or single quotes. To define a multi line string we simply define a string value over multiple lines. We don't have to do any strange concatenation, but simply define the value over multiple lines. DataWeave will add an end-of-line character automatically after each line.

In the following example we use different ways to defined single and multi line strings:

Source

%dw 2.0

import lines from dw::core::Strings

output application/json
---
{
    doubleQuotes: "String value defined using double quotes",
    singleQuotes: 'String value defined using single quotes',

    // Multi line strings can be defined as well.
    multiLineDoubleQuotes: "This is a multi line
string value
with double quotes",
    multiLineSingleQuotes: 'This is a multi line
string value with 
single quotes',

    // We can use the lines function to transform
    // each line into an element in an array.
    multiLines: lines("Multiline
string transformed
to array of strings")
}

Output

{
  "doubleQuotes": "String value defined using double quotes",
  "singleQuotes": "String value defined using single quotes",
  "multiLineDoubleQuotes": "This is a multi line\nstring value\nwith double quotes",
  "multiLineSingleQuotes": "This is a multi line\nstring value with \nsingle quotes",
  "multiLines": [
    "Multiline",
    "string transformed",
    "to array of strings"
  ]
}

Written with DataWeave 2.4.

July 11, 2022

DataWeave Delight: Using Literal Types

DataWeave has a nice language feature called literal types. Literal types are types with a single predefined values and can be defined using a String, Number or Boolean value. So the value of a literal type is a fixed value. We can combine multiple literal types into a new type using a union type to define an enumaration in DataWeave. The enumaration can only be one of the literal types used to define it.
Together with overloaded functions literal types are very useful. We can define a function where one of the input arguments is a literal type to define specific behaviour based on the literal type. Then we can overload the function for other literal types with different behaviour. DataWeave will make sure the correct function is called based on the value of the input argument and how it matches to the literal type value.

In the following example we define four new literal types (North, East, South, West), use a union type to define the enumaration Compass and the overloaded function direction:

Source

%dw 2.0

import capitalize from dw::core::Strings

// String based literal types with one value.
type North = "north"
type East = "east"
type South = "south"
type West = "west"

// Using union types we can use several literal
// types to define a new type like an enumeration.
type Compass = North | East | South | West

// Simple function that will be invoked if the input
// argument is either "north" or "south".
fun direction(direction: North | South): String = 
    "You are walking to " ++ 
    // We can still use String functions, 
    // because the base literal type is String.
    capitalize(direction) ++ 
    " pole."

// Overloaded function to "catch" the other values
// for the literal type Compass: "east" and "west".    
fun direction(direction: Compass): String = "Where are you going?"

// Simple data structure with String values.
var data = { up: "NORTH", right: "EAST", down: "SOUTH", left: "WEST" }

output application/json
---
{
    north: direction("north"),
    east: direction("east"),

    // We can coerce a String value into a literal type.
    south: direction(lower(data.down) as Compass),
    west: direction(lower(data.left) as Compass)
}

Output

{
    "north": "You are walking to North pole.",
    "east": "Where are you going?",
    "south": "You are walking to South pole.",
    "west": "Where are you going?"
}

In the following example we use Number and Boolean literal types. Instead of defining them explicitly as types we use the literal type in the function definitions directly:

Source

%dw 2.0

// Overloaded functions with literal types defined 
// directly at the argument level of the function.
// Here we use a Number literal type.
fun displayItems(items: 0) = "You have no items"
fun displayItems(items: 1) = "You have 1 item"
fun displayItems(items: Number) = "You have " ++ items ++ " items"

// Also Boolean literal types are supported.
// We can combine default argument values
// and overloaded functions.
fun message(value: String, debug: true) = "value - " ++ message(value)
fun message(value: String, debug: Boolean = false) = value

output application/json
---
{
    items0: displayItems(0),
    items1: displayItems(1),
    items10: displayItems(10),

    message: message("DataWeave literal types"),
    messageWithDebug: message("DataWeave literal types", true)
}

Output

{
    "items0": "You have no items",
    "items1": "You have 1 item",
    "items10": "You have 10 items",
    "message": "DataWeave literal types",
    "messageWithDebug": "value - DataWeave literal types"
}   

Written with DataWeave 2.4.

June 27, 2022

DataWeave Delight: Getting The Ordinal Value For A Number

The dw::core::Strings has a lot of functions to deal with strings. One of the functions is ordinalize that takes a number as argument and returns the ordinal value as string.

The following exmaple shows several numbers transformed to their ordinal values:

Source

%dw 2.0

import ordinalize from dw::core::Strings

output application/json
---
{
    "1": ordinalize(1),
    "2": ordinalize(2),
    "3": ordinalize(3),
    "10": ordinalize(10),
    "location": "The restaurant is at the " ++ ordinalize(4) ++ " floor."
}

Output

{
  "1": "1st",
  "2": "2nd",
  "3": "3rd",
  "10": "10th",
  "location": "The restaurant is at the 4th floor."
}

Written with DataWeave 2.4.

DataWeave Delight: Check Type Of Value

To check if a value is of a certain type in DataWeave we must use the is operator. We must specify the type after the is operator and the value before the is operator. For example to check if the value 42 is a Number we write 42 is Number. The result is a Boolean that is either true or false. In the previous example the result is true.

In the following examples we use is on several types:

Source

%dw 2.0

import every from dw::core::Arrays

// Closed object type with two properties alias and name
// that must have the String type.
type User = {|alias: String,name: String|}

// Helper function to check if argument value
// is an Array type and
// only contains elements of type String.
fun checkArrayString(value: Any): Boolean = 
    (value is Array) and
    ((value as Array) every ((element) -> element is String))

// Literal type.
type Language = "DataWeave" | "Java" | "Groovy" | "Clojure"

output application/json
---
{
    string: "DataWeave" is String,
    not_string: 42 is String,
    
    number: 42 is Number,

    // We can also use the Null type to check.
    null_value: null is Null,

    object: {alias: "mrhaki", name: "Hubert A. Klein Ikkink"} is Object,

    // We can check on our own object type.
    user: {alias: "mrhaki", name: "Hubert"} is User,

    // Only the alias property is valid.
    not_user1: {alias: "mrhaki", firstName: "Hubert", lastName: "Klein Ikkink"} is User,

    // User is a closed object so extra properties 
    // don't make it of type User anymore.
    not_user2: {alias: "mrhaki", name: "Hubert", location: "Tilburg"} is User,

    // Type of alias property is not correct.
    not_user3: {alias: 42, name: "Hubert"} is User,
    
    array: ["one", "two"] is Array,

    // To also check the types of the elements
    // we must use an extra check as defined
    // in the checkArrayString helper function.
    array_string: checkArrayString(["one", "two"]),
    not_array_string: checkArrayString([1, 2]),
    not_array: checkArrayString(42),

    // Literal types are supported.
    language: "DataWeave" is Language,
}

Output

{
  "string": true,
  "not_string": false,
  "number": true,
  "null_value": true,
  "object": true,
  "user": true,
  "not_user1": false,
  "not_user2": false,
  "not_user3": false,
  "array": true,
  "array_string": true,
  "not_array_string": false,
  "not_array": false,
  "language": true
}

Written with DataWeave 2.4.

June 26, 2022

DataWeave Delight: Calculating Remainder After Division

To calculate the modulo of two numbers in DataWeave we can use the mod function from the dw::Core module. We provide two arguments where the first argument is the number that needs to be divided by the number provided as second argument. The number that remains after a division of the input arguments is returned as a result.

In the following example we use different arguments for the mod function:

Source

%dw 2.0
output application/json
---
{
    result1: 6 mod 3,
    result2: 5 mod 3,
    result3: 5 mod -3,
    result4: -5 mod 3,
    result5: -5 mod -3,
    result6: 1.5 mod 1,
    result7: 5.095 mod 3
}

Output

{
  "result1": 0,
  "result2": 2,
  "result3": 2,
  "result4": -2,
  "result5": -2,
  "result6": 0.5,
  "result7": 2.095
}

Written with DataWave 2.4.

June 21, 2022

DataWeave Delight: Zipping Arrays

DataWeave has a zip function in the dw::Core module. The function will merge two arrays into a new array. Each element in the new array is also an array and will have a value from the two original arrays from the same index grouped together. So for example we have an input array ["A", "B"] and another input array [1, 2]. The result of the zip function will be [["A", 1], ["B", 2]]. The size of the resulting array is the same as the minimal size of both input arrays. Any value from an array that cannot be merged is simply ignored and left out of the resulting array.

In the following code example we use the zip function for different arrays:

Source

%dw 2.0

import take from dw::core::Arrays

var fruit = ["Apple", "Pear", "Banana"]
var prices = [2.30, 1.82, 2.06]

output application/json
---
{
    // Create new array where each element is
    // an array with first element a fruit and
    // second element a price value. 
    zip: fruit zip prices,

    // When we have an array that contains arrays
    // of 2 items (a pair) we can easily turn it into an object
    // with key/value pairs using reduce.
    zipObj: fruit zip prices 
        reduce ((item, acc = {}) -> acc ++ {(item[0]): item[1]}),

    // The resulting array will have no more items
    // then the smallest array that is used with the zip function.
    // In the following example the second array only has 2 items
    // so the resulting array also has 2 items in total. 
    // The fruit "Banana" is now ignored.
    zipMinimal: fruit zip (prices take 2)
}

Source

{
  "zip": [
    [
      "Apple",
      2.30
    ],
    [
      "Pear",
      1.82
    ],
    [
      "Banana",
      2.06
    ]
  ],
  "zipObj": {
    "Apple": 2.30,
    "Pear": 1.82,
    "Banana": 2.06
  },
  "zipMinimal": [
    [
      "Apple",
      2.30
    ],
    [
      "Pear",
      1.82
    ]
  ]
}

Written with DataWeave 2.4.

June 14, 2022

DataWeave Delight: Measure Function Duration With time And duration Functions

To measure the time it takes to execute a function in DataWeave we can use the time and duration functions from the module dw::util::Timer. Both functions take a zero argument function that needs to be executed as argument (() -> T). But the output of the functions is different. The time function returns a TimeMeasurement object that has a result key containing the result of the function we passed as argument. We also get a start key that has the date and time value when the function gets executed. And finally we have the end key that stores the date and time value when the function is finished. To calculate the total duration time of the function we could use the start and end keys, but when we want the duration time we can better use the duration function. The duration function returns a DurationMeasurement object with also a key result that has the output of the function that is executed. The other key is time and contains the time it took for the function to be executed in milliseconds.

In the following example we use both timer functions together with the wait function. The wait function will wait for the given number of milliseconds before returning a value.

Source

%dw 2.0

import wait from dw::Runtime
import duration, time from dw::util::Timer

output application/json
---
{ 
    // time function returns a new object with
    // keys start, end and result.
    // Keys start and end contain the start and end datetime 
    // before and after the function is executed.
    // The result key has the value of the function.
    time: time(() -> 42 wait 1000),

    // duration function returns a new object with
    // keys time and result.
    // Key duration has the total duration for the
    // function execution in milliseconds.
    // The result key has the value of the function.
    duration: duration(() -> 42 wait 1000)
}

Output

{
  "time": {
    "start": "2022-06-14T04:39:21.582958Z",
    "result": 42,
    "end": "2022-06-14T04:39:22.583079Z"
  },
  "duration": {
    "time": 1000,
    "result": 42
  }
}

Applying the time or duration function in our code is intrusive as the result object is different from the result of our function we want to measure. We can write a helper function that uses the time or duration function, logs the output of the functions using log function and finally still returns the value from our input function by selecting the result key.

In the next example we create the wrapTime and wrapDuration functions that can be used to log the output of the time and duration functions and still return the result of the input function. This way we can introduce timing of function duration in a less intrusive way.

Source

%dw 2.0

import wait from dw::Runtime
import duration, time from dw::util::Timer

fun answer() = 42 wait 400

// We still want the original result from the answer function,
// but also log the output of the time function.
// We pass the output of the time function to the log function that
// will log the output and then return the output value. 
// And finally use the result key from the time function
// to get output from the answer function.
fun wrapTime(f) = log("Time", time(f)).result

// We still want the original result from the answer function,
// but also log the output of the duration function.
// We pass the output of the time function to the log function that
// will log the output and then return the output value. 
// And finally use the result key from the duration function output
// to get output from the answer function.    
fun wrapDuration(f) = log("Duration", duration(f)).result

output application/json
---
{ 
    // Simple invocation of the function answer to get the result.
    result: answer(),

    // Use wrapTime function to still get result, but also log time output.
    resultTimer: wrapTime(() -> answer()),

    // Use wrapDuration function to still get result, but also log duration output.
    resultDuration: wrapDuration(() -> answer())
} 

Output

{
  "result": 42,
  "resultTimer": 42,
  "resultDuration": 42
}

Log output

Time - { start: |2022-06-14T04:52:29.287724Z|, result: 42, end: |2022-06-14T04:52:29.687875Z| }
Duration - { time: 400, result: 42 }

Written with DataWeave 2.4.

March 16, 2022

DataWeave Delight: Partition An Array

In DataWeave we can partition the items in an array using a predicate function by using the partition function from the dw::core::Arrays module. The function takes an array as first argument and a predicate function as second argument. The predicate function should return true or false for each item of the array. The result is an object with the key success containing all items from the array that returned true for the predicate function and a key failure for the items that returned false.

In the following example code we use the partition function on an array:

Source

%dw 2.0

import partition from dw::core::Arrays

var items = ["language", "DataWeave", "username", "mrhaki", "age", 48]

output application/json 
---
{ 
    // Partition by item is of type String or not.
    example1: items partition ((item) -> typeOf(item) == String),

    // Partition by checking if item have value 1 or 4 or not
    // using shorthand notation.
    example2: (0 to 5) partition ([1, 4] contains $)
}

Output

{
  "example1": {
    "success": [
      "language",
      "DataWeave",
      "username",
      "mrhaki",
      "age"
    ],
    "failure": [
      48
    ]
  },
  "example2": {
    "success": [
      1,
      4
    ],
    "failure": [
      0,
      2,
      3,
      5
    ]
  }
}

Written with DataWeave 2.4.

March 15, 2022

DataWeave Delight: Splitting An Array Or Object

The module dw::core::Arrays has extra functions that are useful when working with arrays in DataWeave. In this post we will look at the functions splitAt and splitWhere. We can use these functions to split an array into two arrays. The result of both functions is actually a Pair type, which is defined as an object with the keys l and r. An example is { "l": 1, "r": 2 } which we can read as the left side of the pair has value 1 and the right side of the pair has value 2. The result of the splitAt and splitWhere function will have one part of the split array in the left side of the pair and the rest of the array in the right side.

The splitAt function takes the array as first argument and an index value as second argument. The index value should indicate at which position the array should be split. The function can be used with infix notation as well. The splitWhere function takes as first argument also an array, but the second argument is a predicate function. All items starting from the first item for which the predicate function returns true will be assigned to the r key of the Pair result, and all preceding items to the l key. We can use the infix notation here as well.

In the following example code we use the splitAt and splitWhere function on an array:

Source

%dw 2.0

import splitWhere, splitAt from dw::core::Arrays

output application/json
---
{
    // Split the range at index 4. 
    splitAt: (0 to 8) splitAt 4,

    // Split at the position where the predicate returns true for the first time.
    splitWhere: (0 to 8) splitWhere ((item) -> item > 5)
}

Output

{
  "splitAt": {
    "l": [
      0,
      1,
      2,
      3
    ],
    "r": [
      4,
      5,
      6,
      7,
      8
    ]
  },
  "splitWhere": {
    "l": [
      0,
      1,
      2,
      3,
      4,
      5
    ],
    "r": [
      6,
      7,
      8
    ]
  }
}

Although the function work on arrays we can use them on a object as well. We first turn the object into an array of objects with a single key and value using the pluck function. We can use this array with the splitAt and splitWhere functions. Then we can use the reduce function to transform the values in the Pair to an object with multiple keys and values again.

In the next example we use this mechanism on an object:

Source

%dw 2.0

import splitWhere, splitAt from dw::core::Arrays

// Helper functions
// ----------------
// Transform object to array with key/value pairs
fun objectToArray(obj: Object): Array<Object> = obj pluck ((value, key) -> (key): value)

// Transform array with key/value pairs to object
fun arrayToObject(items: Array<Object>): Object = items reduce ((item, acc = {}) -> acc ++ item)

var obj = {
    language: "DataWeave",
    alias: "mrhaki",
    age: 48, 
    country: "NL"
}

output application/json 
---
{
    // We can use splitAt on object if we first transform an object
    // to an array of key/value pairs, apply the splitAt function and 
    // transform the resulting Pair into an object again.
    splitAtObject: objectToArray(obj) 
        splitAt 3 
        mapObject ((value, key) -> (key): arrayToObject(value)),

    // We can use splitWhere in the same manner for objects.
    splitWhereObject: objectToArray(obj) 
        splitWhere ((item) -> typeOf(item[0]) == Number)
        mapObject ((value, key) -> (key): arrayToObject(value))
}

Output

{
  "splitAtObject": {
    "l": {
      "language": "DataWeave",
      "alias": "mrhaki",
      "age": 48
    },
    "r": {
      "country": "NL"
    }
  },
  "splitWhereObject": {
    "l": {
      "language": "DataWeave",
      "alias": "mrhaki"
    },
    "r": {
      "age": 48,
      "country": "NL"
    }
  }
}

Written with DataWeave 2.4.

March 14, 2022

DataWeave Delight: Importing Functions With An Alias

When we want to reference functions from other modules than the dw::Core module we must us the import statement. We can use the as keyword to map a function name that we want to import to another name that we want to use in our DataWeave code. We provide an alias for the function that we can use in our code. This could be useful if we think our code gets more readable in a certain context by using different function names, but still want to use the original function implementation. Next to providing an alias for function names, we can also provide an alias for a module name, when we use the import <ModuleName> as <NewModuleName> statement. In our DataWeave code we can then refer to the new alias to call functions from the module.

In the following example code we use import using as to define aliases for functions and a module:

Source

%dw 2.0

// Use an alias for functions.
import upper as upperCase from dw::Core
import firstWith as findFirst from dw::core::Arrays

// Assign an alias to a module, so we can reference
// functions from the module as Str::<function>
import dw::core::Strings as Str

output application/json
---
{
    // Use alias upperCase for upper function.
    upperCase: upperCase("DataWeave"),

    // Use alias findFirst for firstWith function.
    findFirst: [1, 2, 3, 4, 5] findFirst ((item, index) -> item > 3),

    // Use function from module with alias.
    str: Str::reverse("DataWeave")
} 

Output

{
  "upperCase": "DATAWEAVE",
  "findFirst": 4,
  "str": "evaeWataD"
}

Written with DataWeave 2.4.

March 1, 2022

DataWeave Delight: Taking or dropping items from an array

The module dw::core::Array has several functions to create a new array by taking or dropping a number of elements of the source array. With the take function has as first input argument the original array and the second is a number indicating the number of items to take. For example [1, 2] take 1 will return [1]. Notice that we can use the functions also in the infix notation. The drop function works similarly, but will skip the number of items we define as second argument to the function. So [1, 2] drop 1 will return [2].

In the following example we use the take and drop function with several numbers to use or skip items from the original array:

Source

%dw 2.0

import take from dw::core::Arrays

var list = ['Simple', 'list', 'with', 5, 'items']

output application/json
---
{
    // Return array with first element "Simple"
    "take 1 item": list take 1,

    // Return array with first 2 elements.
    "take 2 items": list take 2,

    // Empty list returned as we take 0 items.
    "take 0 items": list take 0,

    // Return array with all elements as 6 is larger 
    // than number of items in the list.
    "take 6 items": list take 6
}

Output

{
  "take 1 item": [
    "Simple"
  ],
  "take 2 items": [
    "Simple",
    "list"
  ],
  "take 0 items": [
    
  ],
  "take 6 items": [
    "Simple",
    "list",
    "with",
    5,
    "items"
  ]
}

Source

%dw 2.0

import drop from dw::core::Arrays

var list = ['Simple', 'list', 'with', 5, 'items']

output application/json
---
{
    // Return array where first element of original 
    // array is skipped.
    "drop 1 item": list drop 1,

    // Return array with elements from index 3.
    "drop 3 items": list drop 3,

    // Empty array returned as we drop all items.
    "drop 5 items": list drop 5,

    // Return empty array as 6 is larger than
    // number of items in the list.
    "drop 6 items": list drop 6,

    // Range as array of numbers.
    "drop from range": (0 to 10) drop 8
}

Output

{
  "drop 1 item": [
    "list",
    "with",
    5,
    "items"
  ],
  "drop 3 items": [
    5,
    "items"
  ],
  "drop 5 items": [
    
  ],
  "drop 6 items": [
    
  ],
  "drop from range": [
    8,
    9,
    10
  ]
}

We can also specify a condition using a predicate function to determine how many items to take or drop. In that case we use the takeWhile and dropWhile functions. Instead of specifying a number of items we use predicate function. The predicate function should return true or false. The function is applied to the items of the original array and as long as the return value is true it will be included in the resulting array with takeWhile or skipped with dropWhile.

In the next example we use both functions:

Source

%dw 2.0

import takeWhile, dropWhile from dw::core::Arrays

var list = (0 to 10)

var object = {
    name: "mrhaki", 
    livesIn: "Tilburg", 
    worksAt: 'JDriven'
}

// Helper function to get key from object
// with only 1 key/value pair.
fun getKey(obj: Object): Key = keysOf(obj)[0]

output application/json
---
{
    // Return array with first items smaller than 6.
    take: list takeWhile ((item) -> item < 6),

    // Once a false is returned for the condition
    // no more items are processed.
    // This time we use shorthand notation for the function argument.
    irregularTake: [1, 5, 7, 2, 4, 6] takeWhile $ < 6,

    // Return array where first items smaller than 6 are skipped.
    drop: list dropWhile ((item) -> item < 6),

    // With a bit of transformation we can also use the 
    // functions on objects. 
    // First we transform the object into an array, 
    // apply the function and transform to object again.
    objectTake: object
        pluck ((value, key) -> (key): value)
        takeWhile ((item) -> sizeOf(getKey(item)) == 4)
        reduce ((result, acc = {}) -> acc ++ result),

    objectDrop: object
        pluck ((value, key) -> (key): value)
        dropWhile ((item) -> getKey(item) ~= "name")
        reduce ((result, acc = {}) -> acc ++ result)        
}

Output

{
  "take": [
    0,
    1,
    2,
    3,
    4,
    5
  ],
  "irregularTake": [
    1,
    5
  ],
  "drop": [
    6,
    7,
    8,
    9,
    10
  ],
  "objectTake": {
    "name": "mrhaki"
  },
  "objectDrop": {
    "livesIn": "Tilburg",
    "worksAt": "JDriven"
  }
}

Written with DataWeave 2.4.

February 23, 2022

DataWeave Delight: Reversing order of keys in objects

An object in DataWeave consists of key and value pairs. If we want to create a new object sorted by key or value we can use the orderBy function from the dw::Core module. The orderBy function takes as first argument the object. The second argument is a criteria function that can the value and key to determine how to order the object. But if we want to reverse the ordering of the key and value pairs we cannot simply use the orderBy function, because we don't have access to the index value of the key/value pair in the criteria function. We would love to have that index value, so we could negate the index value to reverse the ordering. What we need to do is first turn our object in an array of objects using the pluck function, where each object has our key and value. We can now use the orderBy function on that array of objects to reverse the order. Finally we need to reduce the array of objects into a new object using the reduce function, where the ordering of keys will be reversed.

In the following example we use :

Source

%dw 2.0

var object = {
    name: "Hubert Klein Ikkink",
    alias: "mrhaki",
    city: "Tilburg"
}

output application/json
---
{
    reverseKeys: object 
        // Turn into array: [{name: "Hubert Klein Ikkink"}, ...]
        pluck ((value, key, index) -> {(key): value})

        // Reverse the ordering based on negated index value
        orderBy ((item, index) -> -index) 

        // Transform array back to an object.
        reduce ((item, accumulator = {}) -> accumulator ++ item)
}

Output

{
  "reverseKeys": {
    "city": "Tilburg",
    "alias": "mrhaki",
    "name": "Hubert Klein Ikkink"
  }
}

Written with DataWeave 2.4.

DataWeave Delight: Reversing the items in an array

To reverse the order of the items in the array we have two possible solutions in DataWeave. The first solution uses the index operator to access items in an array ([...]). We can define a range as value for the index operator to get all elements that have a index value that is part of the range. For example we could use [1 to 3] to get the second up to the fourth items from the array. The value -1 can be used to indicate the last index in the array, so we could get a new array where the items are in reversed order by using the range [-1 to 0].

The second option is to use the orderBy function for an array from the dw::Core module. The function takes as first argument an array and the second argument is a criteria function that is applied for each element. The criteria function will get the array element value and index. The output of the function is used for the ordering of the resulting array. To reverse the order we can negate the index value of the criteria function and we get a new array in reverse order: ((item, index) -> -index).

In the following example we use both ways to reverse the order of an array:

Source

%dw 2.0

var list = [2, 1, 4, 5, 3]

output application/json
---
{
    // Using index operator.
    reverseIndices: list[-1 to 0],

    // Using orderBy with criteria function.
    reverseOrderBy: list orderBy ((item, index) -> -index),

    // Using orderBy with shorthand notation for criteria function.
    reverseOrderByShorthand: list orderBy -$$
}

Output

{
  "reverseOrderBy": [
    3,
    5,
    4,
    1,
    2
  ],
  "reverseIndices": [
    3,
    5,
    4,
    1,
    2
  ],
  "reverseOrderByShorthand": [
    3,
    5,
    4,
    1,
    2
  ]
}

Written with DataWeave 2.4.

February 17, 2022

DataWeave Delight: Removing duplicates from an array

If we want to remove duplicate items from an array in DataWeave we can use the distinctBy function from the dw::Core module. The first argument is an array and the second argument is a criteria function. The criteria function gets an item of the array and the index value of that item in the array. The return value of the function defines the uniqueness of that item. This can simply be the item value itself or something more complex like a calculation or selector.

In the following example we use several criteria functions to remove duplicate items from an array:

Source

%dw 2.0

var items = [
    { id: "DW", name: "DataWeave" },
    { id: "CLJ", name: "Clojure" },
    { id: "DW", name: "DataWeave" }
]
var s = "aabccdeff"

output application/json 
---
{ 
    // An array with numbers where we want to remove duplicates.
    numbers1: [1, 5, 5, 6, 2, 3, 3, 1] distinctBy ((item) -> item),

    // We can also use a bit more complex function to define uniqueness.
    numbers2: [1, -2, 2, 3, -1] distinctBy (item) -> abs(item),

    // We can use shorthand notation $ and
    // distinctBy also works on objects in the array.
    items1: items distinctBy $,

    // Instead of doing a distinctBy on the whole object
    // we can also use matching function and use properties
    // from the object to define unique objects.
    items2: items distinctBy ((item, index) -> item.id),

    // With the help of toArray and joinBy we can remove duplicates
    // from a string value.
    string: dw::util::Coercions::toArray(s) distinctBy $ joinBy ""
}

Output

{
  "numbers1": [
    1,
    5,
    6,
    2,
    3
  ],
  "numbers2": [
    1,
    -2,
    3
  ],
  "items1": [
    {
      "id": "DW",
      "name": "DataWeave"
    },
    {
      "id": "CLJ",
      "name": "Clojure"
    }
  ],
  "items2": [
    {
      "id": "DW",
      "name": "DataWeave"
    },
    {
      "id": "CLJ",
      "name": "Clojure"
    }
  ],
  "string": "abcdef"
}

Written with DataWeave 2.4.

DataWeave Delight: Counting items in array with predicate

The dw::core::Arrays module has some nice functions that works with arrays. One of these functions is countBy. The function takes an array as first argument and a predicate function that accepts an item from the array and should return true or false. The total number of items that adhere to the predicate is the result of the countBy function.

In the following examples we use the countBy function with different predicates:

Source

%dw 2.0

import countBy from dw::core::Arrays

var languages = ['DataWeave', 'Groovy', 'Java', 'Clojure']

output application/json
---
{ 
    // Count items that have more than 6 characters.
    sample1: languages countBy ((item) -> sizeOf(item) > 6), 

    // Count number of even numbers.
    sample2: 1 to 10 countBy (number) -> isEven(number),

    // Using shorthand notation $ to count upper case characters.
    sample3: ["AB", "cD", "Ef", "GH"] countBy dw::core::Strings::isUpperCase($)
}

Output

{
  "sample1": 2,
  "sample2": 5,
  "sample3": 2
}

Written with DataWeave 2.4.