Group sum and transform json object with values in nested array - javascript

I am trying to aggregate and transform the following json :
[
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}]
into an object that is grouped by itemId, and then aggregated by quantity, and aggregated by total cost (item cost * item quantity for each order) by month. Example:
[
{
"itemId": 100,
"period": [
{
"month": "01/17",
"quantity": 12,
"cost": 130
}
]
},
{
"itemId": 101,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 100
},
{
"month": "02/17",
"quantity": 5,
"cost": 100
}
]
},
{
"itemId": 102,
"period": [
{
"month": "01/17",
"quantity": 5,
"cost": 125
},
{
"month": "02/17",
"quantity": 1,
"cost": 25
}
]
}
]
I have a small indention on my desk in which I have been beating my head trying to figure how to do this using native map/reduce or lodash.

You can do like this:
var orders = [{orderId:"01",date:"2017-01-02T06:00:00.000Z",items:[{itemId:100,itemCost:12,itemQuantity:10},{itemId:102,itemCost:25,itemQuantity:4}]},{orderId:"02",date:"2017-01-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]},{orderId:"03",date:"2017-02-08T06:00:00.000Z",items:[{itemId:100,itemCost:15,itemQuantity:2},{itemId:101,itemCost:20,itemQuantity:5},{itemId:102,itemCost:25,itemQuantity:1}]}];
// First, map your orders by items
var items = {};
orders.forEach(function(order) {
// set the month of each order
var month = new Date(order.date);
month = ('0' + (month.getMonth() + 1)).slice(-2) + '/' + String(month.getFullYear()).slice(-2);
// for each item in this order
order.items.forEach(function(item) {
// here we already have both keys: "id" and "month"
// then, we make sure they have an object to match
var id = item.itemId;
if (!items[id]) {
items[id] = {};
}
if (!items[id][month]) {
items[id][month] = { cost:0, quantity:0 };
}
// keep calculating the total cost
items[id][month].cost += item.itemCost * item.itemQuantity;
items[id][month].quantity += item.itemQuantity;
});
});
// Now, we format the calculated values to your required output:
var result = Object.keys(items).map(function(id) {
var obj = {
itemId: id,
period: Object.keys(items[id]).map(function(month) {
items[id][month].month = month;
return items[id][month];
}),
};
return obj;
});
console.log(result);
Hope it helps.

You could use this transformation:
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
const myList = [
{
"orderId" : "01",
"date" : "2017-01-02T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 12,
"itemQuantity": 10
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 4
}
]
},
{
"orderId": "02",
"date" : "2017-01-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
},
{
"orderId": "03",
"date" : "2017-02-08T06:00:00.000Z",
"items" : [
{
"itemId": 100,
"itemCost": 15,
"itemQuantity": 2
},
{
"itemId": 101,
"itemCost": 20,
"itemQuantity": 5
},
{
"itemId": 102,
"itemCost": 25,
"itemQuantity": 1
}
]
}];
const result = Object.values(myList.reduce( (acc, o) => {
const month = o.date.substr(5,2) + '/' + o.date.substr(2,2);
return o.items.reduce ( (acc, item) => {
const it = acc[item.itemId] || {
itemId: item.itemId,
period: {}
},
m = it.period[month] || {
month: month,
quantity: 0,
cost: 0
};
m.cost += item.itemCost * item.itemQuantity;
m.quantity += item.itemQuantity;
it.period[month] = m;
acc[item.itemId] = it;
return acc;
}, acc);
}, {})).map( o =>
Object.assign({}, o, { period: Object.values(o.period) })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

I think the other answers out there do a pretty good job from the vanilla angle, so I wanted to take a stab at a more lodash-intensive approach since you mentioned it as a tag. This is mainly just a fun challenge, but I hope the solution is elegant enough for you to lift components from.
Before we begin, I'll be using both the vanilla lodash module and the functional programming flavor of lodash. Let fp be the functional programming module and _ be vanilla (and let orders be your original data structure). Also, as a challenge, I'll do my best to minimize vanilla JS methods and arrow funcs to maximize lodash methods and function creation methods.
First, let's get all the items in a row, paired with their order information:
const items = _.flatMap(orders, o=> _.map(o.items, i=> [i, o]));
I know I said I wanted to minimize arrow functions, but I couldn't think of any other way to get the order object to the end of the chain. Challenge yourself to rewrite the above in terms of a composition (e.g. fp.compose or _.flow) and see what happens.
I'd say now's as good a time as any to group up our pairs by the item id:
const id_to_orders = _.groupBy(items, fp.get('[0].itemId'));
Here, fp.get('[0].itemId') gives us a function which, given an array, returns the itemId of the first element (in our case, we have a list of pairs, the first element of which is the item, the second of which is the relevant order object). Therefore, id_to_orders is a map from an item's ID to a list of all the times it was ordered.
This id_to_orders map looks pretty close to the data structure we're after. At a high level, all that's left is transforming the order data for each item into the quantity and cost, grouped by month.
const result = _.mapValues(id_map, fp.flow(
// Arrange the item's orders into groups by month
fp.groupBy(month)
// We're done with the order objects, so fp.get('[0]') filters them
// out, and the second function pairs the item's cost and quantity
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
// Sum up the cost (left) and quantity (right) for the item for the month
, fp.reduce(add_pair, [0, 0])))
// These last couple lines just transform the resulting data to look
// closer to the desired structure.
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count}))
));
And the helpers month and add_pair referenced above:
function month([item, order]){
const date = new Date(order.date)
, month = date.getMonth() + 1
, year = date.getFullYear().toString().slice(-2);
return `${month}/${year}`;
}
function add_pair(p1, p2){
return [p1[0] + p2[0], p1[1] + p2[1]];
}
Just out of curiosity (or sadism), let's see what this whole thing would look like chained together as a single pipeline:
const get_order_data = fp.flow(
fp.flatMap(o=> _.map(o.items, i=> [i, o]))
, fp.groupBy(fp.get('[0].itemId'))
, fp.mapValues(fp.flow(
fp.groupBy(month)
, fp.mapValues(fp.flow(
fp.map(fp.flow(fp.get('[0]'), i=> [i.itemCost, i.itemQuantity]))
, fp.reduce(add_pair, [0, 0])))
, _.toPairs
, fp.map(([month, [cost, count]])=> ({month, cost, count})))
));
const result = get_order_data(orders);
You'll notice this composed version has a lot more fp (as opposed to _). If you're curious why it's easier this way, I encourage you to read the lodash FP guide.
jsfiddle with everything.
Finally, if you'd like to transform the result from the code above exactly into the output format you mentioned in your post, here's what I recommend:
const formatted = _.keys(result).map(k=> ({itemId: k, periods: result[k]}));

Related

Getting multiple percentage from a reduce map of a json data

I would like to apologize in advance if there's already question like this but I've been searching all day and I really can find anything.
I call an api that returns a response. Then I only get the details/data that I need and currently now have a json file with this sample value:
{
"total": 563,
"shipping_fee": 58,
"e_charges": ???,
"order_items": [
{
"item_id": 6291020872,
"quantity": 1,
"price": 88,
"total": 88
},
{
"item_id": 7755274567,
"quantity": 1,
"price": 150,
"total": 150
},
{
"item_id": 7980571205,
"quantity": 1,
"price": 45,
"total": 45
},
{
"item_id": 12612977930,
"quantity": 1,
"price": 280,
"total": 280
}
]
}, ... {} {} {}....
My problem is that, I need to get the sum of all total in order_items [] then after that, I need to add the shipping_fee and get 2 sets of percentage
10%
8%
Note that, I'm getting the values in run-time.
const resDetails = await request.post(baseURL, {
data: viewData
});
info = await JSON.parse(JSON.stringify(await resDetails.json()));
This is where the json file came from:
orders = await info.data.orders.map((x) => (total: x.order_items.map(y => Number(y.order_price) * y.amount).reduce((total, y) => y+total),
shipping_fee : Number(x.shipping_fee),
e_charges: Number(Number((x.order_items.map(y=> Number(y.order_price) * y.amount).reduce((total, y) => Number(y+total)) + Number(x.shipping_fee)) * 0.1).toFixed()),
order_items: x.order_items.map((y) => ({
item_id : y.item_id,
quantity: y.amount,
price: Number(y.order_price),
total: Number(y.order_price) * y.amount)}))
I was able to get the 10% but my main problem is adding the other 8% without doing mapping, reducing and adding the shipping_fee again.
Number(Number((x.order_items.map(y=> Number(y.order_price) * y.amount).reduce((total, y) => Number(y+total)) + Number(x.shipping_fee)) * 0.1).toFixed())
I'm fairly new to the javascript/typescript world. I hope someone can help me find a more optimized way to achieve what I need. Thank you.
EDIT:
Here's the actual code:
You may please go through the below code. I'm not a big fan of doing map, reduce and calculations in single line because it takes away the readability. So, I broken down the calculations to a number of methods.
At the end, order total is calculated only once and saved the e_charges percentages in 2 different keys e_charges_8 and e_charges_10, which I believe you can modify as per your need.
let orders = [
{
"shipping_fee": 58,
"order_items": [
{
"item_id": 6291020872,
"quantity": 1,
"price": 88
},
{
"item_id": 7755274567,
"quantity": 1,
"price": 150
},
{
"item_id": 7980571205,
"quantity": 1,
"price": 45
},
{
"item_id": 12612977930,
"quantity": 1,
"price": 280
}
]
},
{
"shipping_fee": 58,
"order_items": [
{
"item_id": 6291020872,
"quantity": 1,
"price": 88
},
{
"item_id": 7755274567,
"quantity": 1,
"price": 150
},
{
"item_id": 7980571205,
"quantity": 1,
"price": 45
},
{
"item_id": 12612977930,
"quantity": 1,
"price": 280
}
]
}
]
const getOrderItemTotal = (orderItem) => {
return orderItem.quantity * orderItem.price
}
const getPercentage = (shippingFee, total, percentage) => {
return ((total + shippingFee) * percentage).toFixed()
}
const getOrderItemsTotal = (orderItems) => {
return orderItems.reduce((total, orderItem) => (total + orderItem.total), 0)
}
orders = orders.map(order => {
order.order_items.map(orderItem => {
orderItem.total = getOrderItemTotal(orderItem)
return orderItem
})
order.total = getOrderItemsTotal(order.order_items)
order.e_charges_8 = getPercentage(order.shipping_fee, order.total, 0.1)
order.e_charges_10 = getPercentage(order.shipping_fee, order.total, 0.08)
return order
})
console.log(orders)

How do I find the latest time for each date in a JSON array?

I have this array in JSON format:
var result=[
{
"index": 13,
"id": 1122,
*
The approach below:
get a unique list of only the dates from the array i.e. dd/mm/yyyy
for each date in the unique list, create a sorted array per the times for that date
return the 0th item from that sorted array for that date
Example code:
var result = [
{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
// get a list of the dates in result
// nothing fancy - the date is just a key
var dates = result.map(k => k.dateTime.substr(0, 10));
// get unique dates from this array
var uniqueDates = Array.from(new Set(dates));
// for each unique date, sort the times descending
// return the first item (latest) for that date
var filtered = uniqueDates.map(ud => {
var dateItems = result.filter(d => d.dateTime.substr(0, 10) == ud);
dateItems.sort((a, b) => (new Date(b.dateTime)).getTime() - (new Date(a.dateTime)).getTime());
return dateItems[0];
});
// output
console.log(filtered);
Sorting the Array on the Date value of dateTime, write a result array with dates-only as keys, retrieve the values of the result.
The TypeError, by the way, is because you should check for i + 1 being smaller than result.length in the loop (this will be falsy for the last element within the loop. In that case new Date(result[i+1].dateTime) will throw the error).
// initialize log helper
const log = Logger();
// create an empty Object
const result = {};
// sort data ascending
const dataSorted = getData().sort((a, b) =>
new Date(a.dateTime) - new Date(b.dateTime));
// add to result with datestring as key.
// The value with the most recent date will be preserved
// because key values are unique (so equal keys are overwritten)
dataSorted.forEach(v => result[new Date(v.dateTime).toDateString()] = v);
// the values of [result] contain the most recent records per date
log(Object.values(result));
// this can also be a one liner, using a reducer method
const resultX = Object.values(
getData()
.sort( (a, b) => new Date(a.dateTime) - new Date(b.dateTime) )
.reduce( (acc, value) =>
({...acc, [new Date(value.dateTime).toDateString()]: value}), {} )
);
log(`\n**from reducer`, resultX);
function getData() {
return [{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
}
function Logger() {
const report = document.querySelector("#report") ||
document.body.insertAdjacentElement(
"beforeend",
Object.assign(document.createElement("pre"), {id: "report"}));
return (...args) => args.forEach(stuff =>
report.textContent += (stuff instanceof Object
? JSON.stringify(stuff, null, 2) : stuff) + "\n");
}
You can group the data based on date like,
const groups = data.reduce((groups, currVal) => {
const date = currVal.dateTime.split(' ')[0];
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(currVal);
return groups;
}, {});
-> Here we split the date and and time part using split(' ') and took the date alone separately and form a group,
const date = currVal.dateTime.split(' ')[0];
And then you can get the recent date and time using the method,
groups[item].reduce((a, b) => (a.dateTime > b.dateTime ? a : b));
Working snippet:
const data = [
{
"index": 13,
"id": 1122,
"price": 100,
"dateTime": "11/12/2020 1:59"
},
{
"index": 14,
"id": 1122,
"price": 300,
"dateTime": "11/12/2020 3:15"
},
{
"index": 15,
"id": 1122,
"price": 314,
"dateTime": "11/13/2020 2:20"
},
{
"index": 16,
"id": 1122,
"price": 280,
"dateTime": "11/13/2020 2:23"
}
];
//Group the data based on date
const groups = data.reduce((groups, currVal) => {
const date = currVal.dateTime.split(' ')[0];
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(currVal);
return groups;
}, {});
//Get the recent date and time based on each group
const result = [];
Object.keys(groups).filter(item => {
const newData = groups[item].reduce((a, b) => (a.dateTime > b.dateTime ? a : b));
result.push(newData);
});
console.log(result)

Sort by the sum of array in object

I am looking for a solution to sort an array by the sum of an array property within an object.
For example if the main array is
[
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
How can I sort the sum of Day to return as
[
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
},
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
}
]
You just need sort your array with comparator, that uses reduce to calc sum of inner array values:
let arr = [{"Grid": {"Day": [11,12]}, "Name": "One"},
{"Grid": {"Day": [5,2]}, "Name": "Two"},
{"Grid": {"Day": [1,2]}, "Name": "Two"}];
let sum = el => el.Grid.Day.reduce((a,b) => a + b);
arr.sort((a,b) => sum(a) - sum(b));
console.log(arr)
You can use a combination of reduce to sum the array, and sort to order the output:
var input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
var result = input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(result);
function sumOfDay(obj){
return obj.Grid.Day.reduce( (acc,curr) => acc + curr, 0);
}
Note that Array.prototype.sort actually mutates the original array in place. so the above could also do
input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(input);
So, don't fall into the trap of thinking the original array is unchanged just because I assigned the result to result!.
If you do wish to sort a copy of the array do this:
var result = input.slice().sort( (a,b) => sumOfDay(a) - sumOfDay(b));
Create a new Array of a by mapping through it and using reduce on the Day Array of Grid to get your sum which you can compare within a sort to return your list sorted by summed days.
const a = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
const daySum = ({Grid}) => Grid.Day.reduce((prev, curr) => prev+curr, 0)
const sorted = [...a].sort(daySum)
console.log(sorted)
console.log(a) //Original array intact
Just "another" approach to solve the issue: assuming you (someday, later, eventually) may need to sort again, a good approach may also be to add a property to each grid item holding the sum of the days, avoiding the .reduce call every time you need to sort the array.
In this approach, .forEach is used to create the new property (through .reduce), and then .sort is used to sort the array in-place.
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
// Add a DaySum property evaluating the sum of the days.
input.forEach(i => i.Grid.DaySum = i.Grid.Day.reduce((a,b) => a + b));
// ^--- the second parameter (initial value) is unneeded here due to the fact that all elements are actually numeric, hence if the initial value is the first element of the array, which is a number already.
// Sor the array by that property.
input.sort((a,b) => a.Grid.DaySum - b.Grid.DaySum);
console.log(input);
Or, as suggested by #Andreas below, you can directly assign the property while sorting:
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
const sum = (a,b) => a + b;
input.sort((a,b) => {
a.Grid.DaySum = a.Grid.DaySum || a.Grid.Day.reduce(sum);
b.Grid.DaySum = b.Grid.DaySum || b.Grid.Day.reduce(sum);
return a.Grid.DaySum - b.Grid.DaySum;
});
console.log(input);

JavaScript: Creating 2 dimensional array of objects based on condition

I'm working on a project that involves two dimensional arrays of objects. I've been working to try to figure out this answer for a while now, and I have some ideas of how to solve it, but I'm a bit stumped.
Let's assume there are animal shelters in City A and that each can hold 50 animals. Animals are coming in from different parts of the state, but the amount of animals from one place can never be more than 50. Here's an example of the animals coming in.
let animal_shelter_capacity =< 50;
let array 1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 },
{ "region": "Central", quantity: 20}
]
In this example, the animals from NE (25) and NW (21) would go to one shelter (46 animals in total), the animals from SE (43) would go to another shelter (43 animals in total), and the animals from SW (18) and Central (20) would go to a third shelter (38 animals in total). The number of animals in one shelter can never be greater than 50.
So, I need to produce an array that looks like this:
let array2 = [
[ { "region": "NE", quantity: 25 }, { "region": "NW", quantity: 21 }],
[ { "region": "SE", quantity: 43 } ],
[ { "region": "SW", quantity: 18 }, { "region": "Central", quantity: 20} ]
]
I'm able to loop through array1 using forEach, but when it comes to adding until a certain value is reached, then creating a new array of arrays, I'm a little stumped on how to proceed to do this.
Here's what I have so far:
let array2 = [] //instantiate array
array1.forEach(function(element, index, array)
{
let sum = 0;
let capacity = 50;
for (let j = 0; j < array1.length; j++)
{
sum += array1[j].quantity;
if (sum >= capacity)
{
//create the new array consisting of the regions, push it into the larger array2
}
}
})
I'm not sure how to continue doing this. I know I need to do the following:
1. find a way to cut off the addition sequence once the quantity reaches 50
2. reset the sequence
3. form the new arrays and push them into a larger array
Can anyone provide any advice on how to proceed?
Try this. Loop through shelters, if it can fit, add it to the current shelter list. If not, save the current shelter roster and start a new one. After the loop, make sure to save the current roster being written
const locations = [{
"region": "NE",
"quantity": 25
},
{
"region": "NW",
"quantity": 21
},
{
"region": "SE",
"quantity": 43
},
{
"region": "SW",
"quantity": 18
},
{
"region": "Central",
"quantity": 20
}
]
const shelterRoster = [];
const capacity = 50;
let count = 0;
for (let location of locations) {
let shelter = shelterRoster[shelterRoster.length - 1];
if (!shelter || count + location.quantity > capacity) {
shelterRoster.push([location]);
count = 0;
} else {
shelter.push(location);
}
count += location.quantity
}
console.log(shelterRoster);
You can approach this with reduce(), using a custom object as the initial accumulator. When the reduce finish, you will need to do an extra line of code for get your final result.
const animal_shelter_capacity = 50;
const array1 = [
{"region": "NE", quantity: 25},
{"region": "NW", quantity: 21},
{"region": "SE", quantity: 43},
{"region": "SW", quantity: 18},
{"region": "Central", quantity: 20}
];
let obj = array1.reduce((res, curr) =>
{
let test = (curr.quantity + res.c >= animal_shelter_capacity);
return {
r: test ? [...res.r, res.a] : res.r,
c: test ? curr.quantity : res.c + curr.quantity,
a: test ? [curr] : [...res.a, curr]
};
},{r:[], c:0, a:[]});
let newArr = [...obj.r, obj.a];
console.log(newArr);
On the previous code, the accumulated object have the next keys:
r: The array of shelters generated progressively.
c: The counter of animals of the current shelter (see next).
a: The current shelter of animals.
When the reduce finish, the last shelter (that on the property a) will not be on the array of shelters. So, we have to put it manually (this is what the extra line does).
The first point I've come up with, is you need to sort input data first. because your given input ( as asked in the question ) is not the only possible way of having data.
You can have some data like:
let array1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "Central", quantity: 20 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 },
]
and in this example, we should have pushed central and SW together, but not sorting the input at first place will result central and SW in different arrays.
So, conclusion. I think this is gonna work:
var make = function( arr ) {
var res = [],
currentArr = [];
arr.forEach( v => {
sum += v.quantity;
if ( sum <= capacity ) {
currentArr.push( v );
} else {
res.push( currentArr );
currentArr = [ v ];
sum = v.quantity;
}
});
res.push( currentArr );
return res;
},
array1 = [
{ "region": "NE", quantity: 25 },
{ "region": "NW", quantity: 21 },
{ "region": "Central", quantity: 20 },
{ "region": "SE", quantity: 43 },
{ "region": "SW", quantity: 18 }
],
sum = 0,
result,
capacity = 50;
array1.sort( ( a, b ) => {
return a.quantity - b.quantity;
});
console.log( array1 );
result = make( array1 );
console.log( result );

How to DRY mapping function?

I have a following data:
var jsonData = {
"data": [
{
"id": 1,
"name": "London",
"date": "2018-04-20",
"temp": 15,
"rain": 2,
"wind": 50,
"humidity" : 80,
}
]
};
and this is the mapping part I have currently coded:
var mainContainer = {
temp : jsonData.data.map (a => a.temp),
rain : jsonData.data.map (a => a.rain), // jsonData.data.map (a => ) being repeated
wind : jsonData.data.map (a => a.wind), // jsonData.data.map (a => ) being repeated
humidity: jsonData.data.map (a => a.humidity) // jsonData.data.map (a => ) being repeated
};
console.log(mainContainer);
Is there any way to DRY the code, so I do not repeat mapping function? The only difference is in property name and mapping part.
Create an array of property names to be mapped
var props = ["temp", "rain", "wind", "humidity" ];
Now iterate this props using reduce
var mainContainer = props.reduce( (a,c) => {
a[c] = jsonData.data.map (s => s[c]);
return a;
}, {});
You can use a loop over to the data array and create a new object accordingly by checking the property already exist or not.
var jsonData = {
"data": [
{
"id": 1,
"name": "London",
"date": "2018-04-20",
"temp": 15,
"rain": 2,
"wind": 50,
"humidity" : 80,
},
{
"id": 1,
"name": "London",
"date": "2018-04-20",
"temp": 25,
"rain": 22,
"wind": 40,
"humidity" : 60,
}
]
};
var res = {};
jsonData.data.forEach((obj)=>{
if(res.temp){
res.temp.push(obj.temp);
} else {
res.temp = [obj.temp];
}
if(res.rain){
res.rain.push(obj.rain);
} else {
res.rain = [obj.rain];
}
if(res.wind){
res.wind.push(obj.wind);
} else {
res.wind = [obj.wind];
}
if(res.humidity){
res.humidity.push(obj.humidity);
} else {
res.humidity = [obj.humidity];
}
});
console.log(res);

Categories

Resources