How to Identify last element in an Array in Dataweave - mule

i have a dataweave expression to create a custom String , but i do not want to append || to the last element of the input array . How can i achieve it ?
%dw 2.0
output application/json
var arr = ["ABC","XYZ"]
---
arr map((item,index) -> {
index: "custom ('" ++ item ++ "'); " ++ "||"
})

Would this help?
Script
%dw 2.0
output application/json
var arr = ["ABC","XYZ"]
---
arr map((item,index) -> {
index: if((index) < (sizeOf(arr)-1)) "custom ('" ++ item ++ "'); " ++ "||" else item
})
Output
[
{
"index": "custom ('ABC'); ||"
},
{
"index": "XYZ"
}
]

You could also try with the following:
%dw 2.0
output application/json
import * from dw::core::Arrays
var arr = ["ABC","XYZ"]
---
take(arr,(sizeOf(arr)-1)) map (
$ ++ "||" // do your manipulation to modify the custom entry
)
take is documented here

Out of curiosity, are you then taking this resulting array and joining it into a string? Your question seems to indicate the end goal of the function is returning a string. If you are, you can just map and then joinBy
%dw 2.0
output application/json
var arr = ["ABC","XYZ", "DKJL"]
---
arr map "custom($($));" joinBy " || "
Outputs
"custom(ABC); || custom(XYZ); || custom(DKJL);"
And if like in Salim's answer you shouldn't be wrapping the last value in custom, you could also take advantage of the default behavior of reduce:
%dw 2.0
output application/json
var arr = ["ABC","XYZ", "DKJL"]
---
arr[-1 to 0] reduce ("custom($($)); || " ++ $$)
Outputs
"custom(ABC); || custom(XYZ); || DKJL"

Related

How to Java code to generate a string from array to DataWeave 2?

Am working on migrating a Mule 3 application to Mule 4.
We are using Java code to generate an Oracle database query
Here is the existing Java function:
public static String generateStringInClause(String tableAlias, String fieldName, List<String> keys) {
String inClause = "";
for (int i = 0; i < keys.size(); i++){
if (i==0) {
inClause += "('" + keys.get(i) + "'";
} else if ((i+1)%10==0) {
inClause += ",'" + keys.get(i) + "')";
} else if (i%10==0) {
inClause += " or "+tableAlias+"."+fieldName+" in ('" + keys.get(i) + "'";
} else if (i == keys.size()-1){
inClause += ",'" + keys.get(i) + "')";
} else {
inClause += ",'" + keys.get(i) + "'";
}
}
if (keys.size() % 10 == 1 || keys.size() == 1) {
inClause = inClause + ")";
}
return inClause;
}
Here are the actual outputs from the Java code when I pass a List of keys of different sizes:
* 21 = ('a0','a1','a2'...'a8','a9') or xyz.abc in ('a10','a11',...'a19') or xyz.abc in ('a20')
* 12 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11')
* 11 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10')
*
* 10 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9')
* 09 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8')
* 01 = ('a0')
Now In Mule 4 we do not want to use any Java methods / classes so need to convert this function into DataWeave
Here is what I have tried:
%dw 2.0
output application/java
var inClause = ""
var size = sizeOf(payload.keys)
fun test() = payload.keys map ((item, index) ->
if (index ==0) ( inClause ++ "('" ++ item ++ "'")
else if (mod((index+1),10)==0) ( "'" ++ item ++ "')")
else if (mod((index),10)==0) ( " or "++ payload.tableAlias ++ "." ++ payload.fieldName ++ " in ('" ++ item ++ "'")
else if (index == (size-1) ) ( "'" ++ item ++ "')")
else ("'" ++ item ++ "'")
)
var result = test() joinBy ","
var result1 = if((mod(size,10) == 1) or (size == 1)) (result ++ ")") else (result)
---
result1
This script works fine and generates the same result when I have a list up to 10 elements. It does not produce the same result as the Java method when I have > 10 elements in the list of keys.
Input to the DataWeave script:
{
"tableAlias": "xyz",
"fieldName": "abc",
"keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10"]
}
In the output there is an additional comma , before or which will cause SQL query to fail.
Actual output:
('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9'), or xyz.abc in ('a10')
You are trying to migrate the logic from the Java method but you need to take into account that Java is an imperative language and DataWeave a functional language. A 1 to 1 migration may not work or be too difficult to maintain. For example the variable inClause does absolutely nothing in the DataWeave script. You can not accumulate a value over a loop like in Java. You should think of what you are trying to achieve as the output and then think on how to express that.
As an example, I choose first separate the keys into blocks of n. I only need a condition to identify the first block. I don't need to transform each value after I know if it is the first block or one of the rest. That way I don't need to concern myself with matching commas or parenthesis. Then I transform each block more naturally in my view. I added an auxiliary function to format the values in each block. I use a reduce() at the end to concatenate the string resulting of each block. The intention of the code should be more clear than in the Java code.
I encapsulated the inputs to the functions in parameters so it is more reusable and clean. The block size is also a parameter. Magic numbers in code are not a good practice.
%dw 2.0
output application/java
import * from dw::core::Arrays
fun quoteArray(a) = "(" ++ (a map ("'" ++ $ ++ "'") joinBy ",") ++ ")"
fun generateStringInClause(tableAlias, fieldName, keys, size) =
keys divideBy size
map ((item, index) ->
if (index == 0) quoteArray(item)
else (" or "++ tableAlias ++ "." ++ fieldName ++ " in " ++ quoteArray(item) )
)
reduce ((item, accumulator="") -> accumulator ++ item)
---
generateStringInClause(payload.tableAlias, payload.fieldName, payload.keys, 10)
Input:
{
"tableAlias": "xyz",
"fieldName": "abc",
"keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","a11", "a12","a13","a14","a15","a16","a17","a18","a19","a20"]
}
Output:
('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11','a12','a13','a14','a15','a16','a17','a18','a19') or xyz.abc in ('a20')
Here is what I came up with. It is similar to aled's version, with small modifications to make the code smaller
%dw 2.0
import divideBy from dw::core::Arrays
output application/java
fun quoteArray(a) = "(" ++ (a map ("'$'") joinBy ",") ++ ")"
fun generateInClause(tableAlias, fieldName, keys, size) =
keys divideBy size
map quoteArray($)
joinBy (" OR $(tableAlias).$(fieldName) IN ")
---
"(" ++ generateInClause(payload.tableAlias, payload.fieldName, payload.keys, 10) ++ ")"

Array in payload is string, need to coerce to array type (DataWeave)

I have a payload I'm receiving from debezium/kafka as
"reasons": "[7,10,9]" with the array as a string.
I need to filter the array to extract when the item is 10 or 11. Since the array is actually a string in the payload I need to coerce it to an array to filter.
This is my current solution, but I feel there has to be a more efficient way:
%dw 2.0
output application/json
var data = payload.payload.after
var reasons = data.reasons replace "[" with "" replace "]" with "" splitBy "," filter ((num, numIndex) -> num != "10" and num != "11")
---
{
"dnsType": if (dnsType[0] == "11") "clinical" else if (dnsType[0] == "10") "non-clinical" else ""
}
If the string content is compatible with a JSON array then you can use the read() function to let DataWeave parse it for you.
Example read(data.reasons,"application/json")

How can I write this expression in DW 2.0 mule 4

I am using this expression in TM component in mule3 and I wants to migrate this in mule 4.Can anyone please help me to solve this issue
(payload splitBy "\n") map using (startIndex = 41,lastIndex = ((sizeOf $) - 1)){
"Data" : $[[startIndex][0] .. [lastIndex][0]]
I am getting missing expression error
using() is deprecated, replace with do-block
Unable to call: splitBy with arguments: (String | Null, "\\n").
Reasons:
Expecting Type: String, but got: Null. |-- From: String |- From: splitBy(text: String, separator: String) ->
Array<String> 4| (payload splitBy "\n") map using (startIndex =
41,lastIndex = ((sizeOf $) - 1)){
^^^^^^^
Expecting Type: String, but got: Null. |-- From: String
Expecting Type: Regex, but got: "\\n". |-- From: Regex |- From: splitBy(text: String, regex: Regex) ->
Array<String> 4| (payload splitBy "\n") map using (startIndex =
41,lastIndex = ((sizeOf $) - 1)){
^^^^^^^
when I tried same expression in mule 4
Thanks In advance to all who can try to help me
As stated you can change your expression to use do instead of using.
The .. will be replaced by to for getting the substring
%dw 2.0
output application/json
---
(payload splitBy "\n") map do {
var startIndex = 41
var lastIndex = sizeOf($) - 1
---
{
"Data" : $[[startIndex][0] to [lastIndex][0]]
}
}
Note that [startIndex][0] is same as startIndex so you can replace $[[startIndex][0] to [lastIndex][0]] with $[startIndex to lastIndex]
Also if the startIndex is really static and do not depend on the element, you can reduce your expression with
%dw 2.0
output application/json
var startIndex = 41
---
payload
splitBy "\n"
map {
"Data" : $[startIndex to (sizeOf($) -1)]
}

Sorting the string with comma separated numbers and assign sequence

I have a string value coming as "AH-0000006620, AH-0000006619, AH-0000006621", where I need to Remove the Prefix and the remaining numbers "0000006620, 0000006619, 0000006621" to be sorted in Ascending order and I want to store the sequence number for each of them like
for 0000006619 value to be 1
for 0000006620 value to be 2
for 0000006621 value to be 3
So that when I send the info to target system, while iterating the array, for the first element if it is 0000006620 then I will pass the value as 2 for the other sequence tag.
Please let me know how I can achieve this.
You can tweak this though this should serve as a good starting point to what you are looking for:
Script
%dw 2.0
import * from dw::core::Strings
output application/json
var inp = "AH-0000006620,AH-0000006619,AH-0000006621"
---
{((inp replace"AH-" with "") splitBy "," map ($ replace /^0+/ with "") orderBy $ map {
(leftPad($ as String,10,0)) : ($$)
})}
You can do it this way as well.
{((inp replace"AH-" with "") splitBy "," orderBy $ as Number map {
($) : ($$)
})}
**Output**
[
{
"0000006619": 0
},
{
"0000006620": 1
},
{
"0000006621": 2
}
]

How to replace specific value in Json based on condition in Mule(3) Dataweave 1.0

I have a Json Input like below, where if doa is null or "" it needs to replace with the value test if it contains Value then no change required. I have achieved in dwl 2 version using update. Using mabObject in dataweave 1.0 version, but it is not working out. Looking for your expertise. Thanks
Input:
{
"sets": {
"exd": {
"cse": {
"doa": null
}
}
}
}
Achieved in dwl 2 :
%dw 2.0
output application/json skipNullOn="everywhere"
import * from dw::util::Values
---
if
(!isEmpty (payload.sets.exd.cse.doa )) payload
else
payload update
{
case .sets.exd.cse.doa! -> "test"
}
Response
{
"sets": {
"exd": {
"cse": {
"doa": "test"
}
}
}
}
Looking for Mule 3 dataweave 1.0 version of it??
This is a lesser backport of the update operator in DataWeave 2.0 to DataWeave 1.0 as a function. The keys parameter is the sequence of keys as an array as strings. I added a check for key null or empty as it can be done in DataWeave 1.0.
%dw 1.0
%output application/json
%function updateKey(o,keys,value)
o match {
:object -> o mapObject ($$):
updateKey($, keys[1 to -1], value )
when ($$ ~= keys[0] and ((sizeOf keys) >1))
otherwise ($
when ((sizeOf keys) >1) otherwise value
),
default -> $
}
---
updateKey(payload, ["sets", "exd", "cse", "doa"], "test") when (payload.sets.exd.cse.doa is :null or payload.sets.exd.cse.doa == "") otherwise payload
If / else statements work considerably differently in dataweave 1.0, update doesn't exist, and I don't know if isEmpty exists as part of core - don't think so. If you know ahead of time the structure of the object, you can simply build a new object like this:
%dw 1.0
%input payload application/json
%output application/json
---
payload when (payload.sets.exd.cse.doa != '' and payload.sets.exd.cse.doa != null)
otherwise {
sets: {
exd: {
cse: {
data: "test"
}
}
}
}
As per my comment to #aled's solution, here is an update that will limit the change to the specific key even if there are multiple key-value pairs at the target level:
%dw 1.0
%output application/json
%function updateKey(o,keys,value)
o match {
:object -> o mapObject ($$):
updateKey($, keys[1 to -1], value )
when ($$ ~= keys[0] and ((sizeOf keys) >1))
otherwise ($ when (((sizeOf keys) >1) or '$$' != keys[0])
otherwise value
),
default -> $
}
---
updateKey(payload, ["sets", "exd", "cse", "doa"], "test") when (payload.sets.exd.cse.doa is :null or payload.sets.exd.cse.doa == "") otherwise payload