I try to explain you, I have an Array of Objects:
Input:
[
{ "number": "000001", "price": 14.99, "qty": 18, "line": "3" },
{ "number": "000003", "price": 24.0, "qty": 18, "line": "3" },
{ "number": "000004", "price": 18.0, "qty": 18, "line": "3" }
]
This payload is an example. The array can have more or less "qty" or objects with a similar schema. I am trying to separate the objects dynamically into a new array of objects. The validations are:
I need to append in a new array all the objects that do not pass the sum > 700. This operation is (price * qty)
.
If we follow the above example:
(14.99 * 18) = 269.82
does not exceed 700."000003"
has a sum (24 * 18) = 432
. If we add the first and the second, it gives a total of 701.82
. In this case, we need to reduce the qty
by 1, resulting in:[
{ "number": "000001", "price": 14.99, "qty": 18, "line": "3" },
{ "number": "000003", "price": 24.0, "qty": 17, "line": "3" }
]
The next objects inside the array follow the same logic:
[
{ "number": "000003", "price": 24.0, "qty": 1, "line": "3" },
{ "number": "000004", "price": 18.0, "qty": 18, "line": "3" }
]
Output:
[
{
"object": 1,
"total": 677.82,
"items": [
{ "number": "000001", "price": 14.99, "qty": 18, "line": "3" },
{ "number": "000003", "price": 24.0, "qty": 17, "line": "3" }
]
},
{
"object": 2,
"total": 348.00,
"items": [
{ "number": "000003", "price": 24.0, "qty": 1, "line": "3" },
{ "number": "000004", "price": 18.0, "qty": 18, "line": "3" }
]
}
]
Summary of the result:
"object"
is an iterator or index."total"
is the sum (price * qty)
for all elements in "items"
."items"
is the result I explained before.Note: This is an example. If I increase maxTotal
to 1100, the result will be the same because it does not exceed maxTotal
. But if I reduce maxTotal
to 200, the final result will be divided into 5 similar objects.
This is my DWL code, and I don't know how to finish it. I have the idea, but I can't complete it:
%dw 2.0
import * from dw::core::Arrays
output application/json
var inputArray = [
{ "number": "000001", "price": 14.99, "qty": 18, "line": "3" },
{ "number": "000003", "price": 24.0, "qty": 18, "line": "3" },
{ "number": "000004", "price": 18.0, "qty": 18, "line": "3" }
]
var maxTotal = 700
var listOfItems = flatten(inputArray map (itm, idx) -> do {
var result = 1 to itm.qty as Number map (i) -> ({
"number": itm.number,
"price": itm.price,
"qty": 1,
"line": itm.line
})
---
result
})
var divideTo = ((sum(listOfItems.price) / maxTotal) as String {format: "0", roundMode: "UP"}) as Number
---
1 to divideTo map (itm, idx) -> do {
var total = 0
var items = {}
---
{
object: (idx + 1),
total: total,
items: items
}
}
Explanation of the code:
"listOfItems"
divides the objects by qty
, meaning [ "number": "000001", "number": "000001"... n ]
."divideTo"
, I get the value of the total objects in the new array.If you have a better idea to complete the code, I would appreciate your help.
---- New Changes ----
[
{
"object": 1,
"total": 269.82,
"items": [
{
"number": "000001",
"price": 14.99,
"qty": 18,
"line": "3"
}
]
},
{
"object": 2,
"total": 432,
"items": [
{
"number": "000003",
"price": 24,
"qty": 18,
"line": "3"
}
]
},
{
"object": 3,
"total": 324,
"items": [
{
"number": "000004",
"price": 18,
"qty": 18,
"line": "3"
}
]
}
]
I have came up with a slightly different approach which is explained below. I am using the term "group" to denote each item in the expected output array (as they are basically a group of input items)
reduce
.runningResults
.object
index for each group.%dw 2.0
import update from dw::util::Values
output application/json
// To add the current item to the interim results
fun addToResults(runningResults, currentItem) = do {
var currentItemTotal = itemTotal(currentItem)
var lastGroupTotal = runningResults[-1].total
// Split the item depending on the remaining space in the last group of the runningResults
var splittedItem = splitItem(currentItem, maxTotal - lastGroupTotal)
---
(
// Add the eligible portion of the item to the last group if there is quantity left
if(splittedItem.eligible.qty > 0)
runningResults updateLastWithItem splittedItem.eligible
else runningResults
)
then ((
// If there is any remaining portion of the item, start a new group
if(splittedItem.remaining.qty > 0)
($ << defaultGroup) addToResults splittedItem.remaining
else $
))
}
fun itemTotal(item) = item.qty * item.price
// Splits an item into an eligible and a remaining portion based on the remainingTotal in the last group of runningResults
fun splitItem(item, remainingTotal) = do {
var eligibleQty =
if(remainingTotal >= maxTotal and item.price > maxTotal) 1 // to handle the case when price of item can exceed maximum total allowed for group
else min([floor(remainingTotal / item.price), item.qty]) // used min function to avoid exceeding the maximum available quantity. (which can be the case for the first object in result)
---
{
eligible: item update "qty" with eligibleQty,
remaining: item update "qty" with $ - eligibleQty
}
}
// Updates the last group with a new item, updating the total and adding the item in items array
fun updateLastWithItem(runningResults, itemToAdd) =
runningResults update (sizeOf(runningResults) - 1) with (
$ update {
case .total -> ($ + itemTotal(itemToAdd))
case .items -> $ << itemToAdd
}
)
// Default structure for each group in the result
var defaultGroup = {total: 0, items: []}
var maxTotal = 700
---
payload reduce ((item, results = [defaultGroup]) ->
results addToResults item
) map {
// Add an object index to each group
object: $$ + 1,
($)
}
Note