How to add cumulative values in a JSON object in ES6? - javascript

I'm trying to add cumulative values in my JSON object. When I tried using reduce it's summing up all the values of particular key value. How can I achieve my desired output?
Sample JSON
[{
"id": 28,
"Title": "A",
"Price": 10
}, {
"id": 56,
"Title": "AB",
"Price": 10
}, {
"id": 89,
"Title": "ABC",
"Price": 10
}]
required output
[{
"id": 28,
"Title": "A",
"Total_Spent": 10 (Sum of A)
}, {
"id": 56,
"Title": "AB",
"Total_Spent": 20 (sum of A+AB)
}, {
"id": 89,
"Title": "ABC",
"Total_Spent": 30 (sum of A + AB + ABC)
},
.......]

Have a variable to hold the cumulative sum; map each original item to a new one, deconstructing the original and constructing the new item, with Total_Spent having the value of the cumulative sum, which gets updated with each item's price.
const data = [{
"id": 28,
"Title": "A",
"Price": 10
}, {
"id": 56,
"Title": "AB",
"Price": 10
}, {
"id": 89,
"Title": "ABC",
"Price": 10
}];
let cumsum = 0;
const newData = data.map(({id, Title, Price}) => ({id, Title, Total_Spent: cumsum += Price}));
console.log(newData);

I would use array reduce for this task.
console.log([
{ id: 28, Title: "A", Price: 10 },
{ id: 56, Title: "AB", Price: 10 },
{ id: 89, Title: "ABC", Price: 10 }
].reduce((acc, { Price, ...item }, index) => {
acc.push({
...item,
Total_Spent: Price + (acc[index - 1]?.Total_Spent ?? 0)
});
return acc;
}, []));

Related

Transform array of data into grouped data for SectionList component

I'll freely admit that Javascript is not my strongest language, and React Native is very new, so, there may be an obviously easy way to do this that I'm not seeing.
I've got an API that presents some transaction data in a simple structure:
[
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
I want to present this data using a SectionList component, with the transactions in sections by date. My (likely crude) attempt to solve this was going to be to transform this data into the following structure:
[
{
"date": "2021-09-10",
"transactions": [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
}
]
},
{
"date": "2021-09-09",
"transactions": [
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
}
]
},
{
"date": "2021-09-07",
"transactions": [
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
}
]
But I'm honestly lost as to how to transform this data (or if there's a better way to solve this problem). I started by using Lodash's groupBy function, which seemed promising, but it looks like SectionList doesn't want an object, it wants an array.
Transforming the output of groupBy into an array straight off drops the keys and I've got grouped data but no clear value for the section header.
Again, there's probably some deviously simple way to address this, data comes in as a flat array all the time. I appreciate any guidance, assistance, or examples anybody can point me to.
const input = [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
]
const result = input.reduce((accum, current)=> {
let dateGroup = accum.find(x => x.date === current.date);
if(!dateGroup) {
dateGroup = { date: current.date, transactions: [] }
accum.push(dateGroup);
}
dateGroup.transactions.push(current);
return accum;
}, []);
console.log(result)
Given an array, whenever your result is expecting to have same number of elements, use map, but since your result has different number of elements, use reduce as shown above. The idea is by having reduce, loop over each element, see if you can find the element, and push the current element into the list
The lodash groupBy just helps you with group data, you should process grouped data by converting it into your format.
const input = [
{
"id": 1,
"title": "Apple Store",
"date": "2021-09-10",
"amount": "$100.00",
},
{
"id": 41,
"title": "Zulauf, Walter and Metz",
"date": "2021-09-10",
"amount": "$14.00",
},
{
"id": 9,
"title": "Aufderhar PLC",
"date": "2021-09-09",
"amount": "$78.00",
},
{
"id": 10,
"title": "Bayer and Sons",
"date": "2021-09-07",
"amount": "$67.00",
}
];
const groupedArray = _.groupBy(input, "date");
let result = [];
for (const [key, value] of Object.entries(groupedArray)) {
result.push({
'date': key,
'transactions': value
})
}
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
simply
const data =
[ { id: 1, title: 'Apple Store', date: '2021-09-10', amount: '$100.00' }
, { id: 41, title: 'Zulauf, Walter and Metz', date: '2021-09-10', amount: '$14.00' }
, { id: 9, title: 'Aufderhar PLC', date: '2021-09-09', amount: '$78.00' }
, { id: 10, title: 'Bayer and Sons', date: '2021-09-07', amount: '$67.00' }
]
const res = Object.entries(data.reduce((r,{id,title,date,amount})=>
{
r[date] = r[date] ?? []
r[date].push({id,title,date,amount})
return r
},{})).map(([k,v])=>({date:k,transactions:v}))
console.log( res )
.as-console-wrapper { max-height: 100% !important; top: 0 }
With lodash you can group by the date then map to the required form:
const input = [{"id":1,"title":"Apple Store","date":"2021-09-10","amount":"$100.00"},{"id":41,"title":"Zulauf, Walter and Metz","date":"2021-09-10","amount":"$14.00"},{"id":9,"title":"Aufderhar PLC","date":"2021-09-09","amount":"$78.00"},{"id":10,"title":"Bayer and Sons","date":"2021-09-07","amount":"$67.00"}];
const result = _.map(
_.groupBy(input, 'date'),
(transactions, date) => ({ date, transactions })
)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
you could use loadash
var result = _(data)
.groupBy(item => item.date)
.map((value, key) => ({date: key, transactions: value}))
.value();

Count key values from a nested object array

I have an array like this:
[
{
"costs": [{
"value": "80"
}],
"id": 4,
"name": "Subscription Fee",
"month": "March"
},
[
{
"costs": [{
"value": "200"
}],
"id": 2,
"name": "Tution",
"month": "March"
},
{
"costs": [{
"value": "10"
}],
"id": 11,
"name": "DEMO"
}
]
]
I need to have sumation of all the values from costs. How can i do that?
const data = [
{"costs":[{"value":"80"}],"id":4,"name":"Subscription Fee","month":"March"},
[
{"costs":[{"value":"200"}],"id":2,"name":"Tution","month":"March"},
{"costs":[{"value":"10"}],"id":11,"name":"DEMO"}
]
];
// flatten the arrays to get a list of objects
// iterate over this list
const res = data.flat().reduce((total, { costs = [] }) => {
// add the values of this item's costs with total
costs.forEach(({ value = 0 }) => total += +value);
return total;
}, 0);
console.log(res);

Group an array of objects based on a specific field

I have an array of objects. I want to group them by a specific field.
[
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
]
The output I expect for grouping by name attribute is as follows.
{
// Group #1
"JOHN": [
{
"type": 2,
"sum": 200
}
{
"type": 1,
"sum": 5
}
],
// Group #2
"SERA":[
{
"type": 1,
"sum": 43
},
{
"type": 2,
"sum": 129
},
]
}
I used nested loops, but unfortunately the execution speed was slow and it did not give the right results.
As if you mentioned, we can use an object instead of an array for the most outer wrapper. And also swap inside one object to an array, then this is a possible solution.
var data = [{"name": "JOHN","type": 1,"sum": 5},{"name": "SERA","type": 1,"sum": 43},{"name": "SERA","type": 2,"sum": 129},{"name": "JOHN","type": 2,"sum": 200}];
var newData = {};
data.forEach( (item) => {
if (!(item['name'] in newData)) {
newData[item['name']] = [];
}
newData[item['name']].push(
{
'type': item['type'],
'sum' : item['sum']
}
);
});
console.log(newData);
Your proposed output structure is not valid, however using Array.reduce you can create an object in which all the properties are arrays of objects:
const data = [
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
];
const result = data.reduce((c, {name, type, sum}) => {
c[name] = c[name] || [];
c[name].push({type, sum});
return c;
}, {});
console.log(result);
One more way with forEach, destructuring and ?? operator
const merge = (arr) => {
const obj = {};
arr.forEach(({ name, ...rest }) => (obj[name] ??= []).push(rest));
return obj;
};
const data = [
{
name: "JOHN",
type: 1,
sum: 5,
},
{
name: "SERA",
type: 1,
sum: 43,
},
{
name: "SERA",
type: 2,
sum: 129,
},
{
name: "JOHN",
type: 2,
sum: 200,
},
];
console.log(merge(data));
You can use this function which take advantage of Array.prototype.reduce to transform the initial data to another structure of array.
let data = [
{
"name": "JOHN",
"type": 1,
"sum": 5
},
{
"name": "SERA",
"type": 1,
"sum": 43
},
{
"name": "SERA",
"type": 2,
"sum": 129
},
{
"name": "JOHN",
"type": 2,
"sum": 200
}
];
function groupedBy(data, field) {
let fieldValues = [...data].reduce((acc, current) => {
return acc.concat(current[field]);
}, []).filter((value, index, self) => {
return self.indexOf(value) === index;
});
let results = fieldValues.reduce((acc, item) => {
let items = [...data].filter(el => {
return el.name === item;
});
items.forEach(i => delete i.name);
return Object.assign(acc, { [item]: items});
}, {});
return results;
}
console.log(groupedBy(data, "name"));

Javascript: Collapse array of objects on property value, summing quantity property, retaining most recent

Looking for a more "functional" way of achieving this...
I have an object of products that looks like this (note duplicate skuid's)
"products": [
{
"skuid": "B1418",
"name": "Test Product 1",
"price": 7,
"lastOrderedDate": 20181114,
"quantity": 2
},{
"skuid": "B3446",
"name": "Test Product 2",
"price": 6,
"lastOrderedDate": 20190114,
"quantity": 2
},{
"skuid": "B1418",
"name": "Test Product 1",
"price": 7,
"lastOrderedDate": 20180516,
"quantity": 5
},{
"skuid": "B3446",
"name": "Test Product 2",
"price": 6,
"lastOrderedDate": 20180411,
"quantity": 11
}
]
I want to create a new array that has a single object for each distinct skuid but that SUMS all the quantity values and retains the newest lastOrderedDate.
The final result would look like:
"products": [
{
"skuid": "B1418",
"name": "Test Product 1",
"price": 7,
"lastOrderedDate": 20181114,
"quantity": 7
},{
"skuid": "B3446",
"name": "Test Product 2",
"price": 6,
"lastOrderedDate": 20190114,
"quantity": 13
}
]
I can do it with a bunch of forEach's and if's, but I'd like to learn a more concise way to do it. Perhaps with a sort, then reduce?
You can do that in following steps:
Create an object using reduce() whose keys will unique skuid and value will fist object with that skuid
Use forEach on the array and increase the quantity property of corresponding object in object created object.
Use Object.values() to get an array.
const products = [ { "skuid": "B1418", "name": "Test Product 1", "price": 7, "lastOrderedDate": 20181114, "quantity": 2 },{ "skuid": "B3446", "name": "Test Product 2", "price": 6, "lastOrderedDate": 20190114, "quantity": 2 },{ "skuid": "B1418", "name": "Test Product 1", "price": 7, "lastOrderedDate": 20180516, "quantity": 5 },{ "skuid": "B3446", "name": "Test Product 2", "price": 6, "lastOrderedDate": 20180411, "quantity": 11 } ]
const res = products.reduce((ac,a) => (!ac[a.skuid] ? ac[a.skuid] = a : '',ac),{})
products.forEach(x => res[x.skuid].quantity += x.quantity)
console.log(Object.values(res))
You could take a Map and get all values later as result set.
const
getGrouped = (m, o) => {
var item = m.get(o.skuid);
if (!item) return m.set(o.skuid, Object.assign({}, o));
if (item.lastOrderedDate < o.lastOrderedDate) item.lastOrderedDate = o.lastOrderedDate;
item.quantity += o.quantity;
return m;
};
var data = { products: [{ skuid: "B1418", name: "Test Product 1", price: 7, lastOrderedDate: 20181114, quantity: 2 }, { skuid: "B3446", name: "Test Product 2", price: 6, lastOrderedDate: 20190114, quantity: 2 }, { skuid: "B1418", name: "Test Product 1", price: 7, lastOrderedDate: 20180516, quantity: 5 }, { skuid: "B3446", name: "Test Product 2", price: 6, lastOrderedDate: 20180411, quantity: 11 }] },
result = Array.from(data.products
.reduce(getGrouped, new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do it like this:
const DATA = [
{
"skuid": "B1418",
"name": "Test Product 1",
"price": 7,
"lastOrderedDate": 20181114,
"quantity": 2
},{
"skuid": "B3446",
"name": "Test Product 2",
"price": 6,
"lastOrderedDate": 20190114,
"quantity": 2
},{
"skuid": "B1418",
"name": "Test Product 1",
"price": 7,
"lastOrderedDate": 20180516,
"quantity": 5
},{
"skuid": "B3446",
"name": "Test Product 2",
"price": 6,
"lastOrderedDate": 20180411,
"quantity": 11
}
];
const mergeStrategy = {
quantity: (a, b) => a + b,
lastOrderedDate: (a, b) => Math.max(a, b)
}
const mergeByStrategy = strat => a => b => {
const c = Object.assign({}, a, b);
return Object.keys(strat).reduce((acc, k) => {
acc[k] = strat[k](a[k], b[k]);
return acc;
}, c);
}
const groupByWith = prop => merge => xs => xs.reduce((acc, x) => {
if (acc[x[prop]] === void 0) { acc[x[prop]] = x; }
else { acc[x[prop]] = merge(acc[x[prop]])(x); }
return acc;
}, {});
const mergeFunc = mergeByStrategy(mergeStrategy);
const groupData = groupByWith('skuid')(mergeFunc);
console.log(Object.values(groupData(DATA)))
mergeStrategy defines how certain properties are merged. You can define as much properties/functions here as you like.
The mergeByStrategy function first takes a strategy (see above), and then two objects to merge. Note that it takes the objects in a curried form. It creates a shallow copy of both objects which it modifies according to the given strategy. This ensures your original data is still intact.
The groupByWith function takes a property name, a merging function and an array of objects and creates a dictionary/POJO/hashmap/call-as-you-like where each object is stored by the given property name. If there already exists an entry in the dictionary, it uses the merging function to combine the existing entry with the new entry, otherwise is simply stores the new entry into the object.

How to move element in nested array

i wanted to move element in nested array. so, here this my data:
let products = [
{
"product_name": "A",
"_id": "5ace995c14a759325776aab1",
"transactions": [
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 100,
"price": 2000
},
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 80,
"price": 1500
},
]
},
{
"product_name": "B",
"_id": "5ace995914a759325776aab0",
"transactions": [
{
"_id": "5ad3a274ac827c165a510f9b",
"qty": 80,
"price": 1500
}
],
}
]
The output that i expected:
[
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 100,
"price": 2000,
"product_name": "A",
},
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 80,
"price": 1500,
"product_name": "A",
},
{
"_id": "5ad3a274ac827c165a510f9b",
"qty": 80,
"price": 1500,
"product_name": "B",
}
]
then, my solve code:
function move() {
var result = []
for (product of products) {
for (transaction of product.transactions) {
transaction.product_name = product.product_name
result.push(transaction)
}
}
return result
}
product = move()
Is there any effective way to create the output, maybe with array map or anything else? Thank you.
You could flat the transactions with Array#reduce and using Object.assign for adding product_name.
Also used:
destructuring assignment for the properties and
short hand properties for taking a variable as property with the name as key.
var products = [{ product_name: "A", _id: "5ace995c14a759325776aab1", transactions: [{ _id: "5ad3a274ac827c165a510f99", qty: 100, price: 2000 }, { _id: "5ad3a274ac827c165a510f99", qty: 80, price: 1500 }] }, { product_name: "B", _id: "5ace995914a759325776aab0", transactions: [{ _id: "5ad3a274ac827c165a510f9b", qty: 80, price: 1500 }] }],
result = products.reduce((r, { transactions, product_name }) =>
r.concat(transactions.map(t => Object.assign({}, t, { product_name }))),
[]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can reduce and map the transactions to add the product name
let result = products.reduce((c,v)=>{ //Loop the array using reduce
let transactions = v.transactions.map(o=>{ //Loop thru each transactions using map
return Object.assign(o,{"product_name":v.product_name}); //Clone the transaction and add the property product_name
});
return c.concat(transactions); //Merge the current array and the transactions
},[]);
Here is a snippet:
//Your array
let products=[{"product_name":"A","_id":"5ace995c14a759325776aab1","transactions":[{"_id":"5ad3a274ac827c165a510f99","qty":100,"price":2000},{"_id":"5ad3a274ac827c165a510f99","qty":80,"price":1500},]},{"product_name":"B","_id":"5ace995914a759325776aab0","transactions":[{"_id":"5ad3a274ac827c165a510f9b","qty":80,"price":1500}],}]
//The short version
let result = products.reduce((c, v) => c.concat(v.transactions.map(o =>Object.assign(o, {"product_name": v.product_name}))), []);
console.log(result);
Just using js methods you can have your desired output
const products = [
{
"product_name": "A",
"_id": "5ace995c14a759325776aab1",
"transactions": [
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 100,
"price": 2000
},
{
"_id": "5ad3a274ac827c165a510f99",
"qty": 80,
"price": 1500
},
]
},
{
"product_name": "B",
"_id": "5ace995914a759325776aab0",
"transactions": [
{
"_id": "5ad3a274ac827c165a510f9b",
"qty": 80,
"price": 1500
}
],
}
]
let output =[];
products.forEach(elm => elm.transactions.forEach(transaction => {
transaction.product_name = elm.product_name;
output.push(transaction)}));
console.log(output);

Categories