Reduce array differentiating by category in reactjs - javascript

I'm developing an app in reactjs and I have the array:
array = [
{
"id": 1,
"categoryId": 2,
"period": "202101",
"type": "A",
"price": 100,
"discount": 0
},
{
"id": 2,
"categoryId": 2,
"period": "202102",
"type": "B",
"price": 300,
"discount": 20
},
{
"id": 3,
"categoryId": 2,
"period": "202103",
"type": "B",
"price": 200,
"discount": 70
},
{
"id": 4,
"categoryId": 2,
"period": "202104",
"type": "A",
"price": 100,
"discount": 50
},
]
and I need to reduce it to show it as the table:
what I did to show the detail of the prices per period:
const items = array.reduce((acc, e) => {
if (!acc[e["categoryId"]]) {
acc[e["categoryId"]] = {
[e["period"]]: e["price"]
}
} else {
acc[e["categoryId"]][e["period"]] = e["price"]
}
return acc
}, {})
const periods = [...new Set(Object.keys(items).map(i => Object.keys(items[i])).flat())]
thead:
<tr>{dates.map(date => <th>{date}</th>)}</tr>
tbody:
Object.keys(items).map((item) => {
return (
<tr>
<td>{item}</td>
{periods.map((period) => <td>{items[item][period] || ''}</td>)}
</tr>
)
})
but it is only showing the price for each period. I need to show discount and type as well.
What changes are needed, any suggestion?

I think I didn't understand your needs well,
but this is what I did according to your description:
array.reduce((acc, curr) => {
if (!acc[curr["categoryId"]]) {
acc[curr["categoryId"]] = {
[curr["period"]]: {
"price": curr["price"],
"type": curr["type"],
"discount": curr["discount"]
}
}
} else {
acc[curr["categoryId"]][curr["period"]] = {
"price": curr["price"],
"type": curr["type"],
"discount": curr["discount"]
}
}
return acc;
}, {})
And the result of this reduce is:
{
"2": {
"202101": {
"price": 100,
"type": "A",
"discount": 0
},
"202102": {
"price": 300,
"type": "B",
"discount": 20
},
"202103": {
"price": 200,
"type": "B",
"discount": 70
},
"202104": {
"price": 100,
"type": "A",
"discount": 50
}
}
}

what you are looking for is grouping the items in arrays and display them.:
let array = [
{
id: 1,
categoryId: 2,
period: "202101",
type: "A",
price: 100,
discount: 0
},
{
id: 2,
categoryId: 2,
period: "202102",
type: "B",
price: 300,
discount: 20
},
{
id: 3,
categoryId: 2,
period: "202103",
type: "B",
price: 200,
discount: 70
},
{
id: 4,
categoryId: 2,
period: "202104",
type: "A",
price: 100,
discount: 50
}
];
let dates = array.map((e) => <th>{e.period}</th>);
let prices = array.map((e) => <td>{e.price}</td>);
let discounts = array.map((e) => <td>{e.discount}</td>);
let types = array.map((e) => <td>{e.type}</td>);
function App() {
return (
<div className="App">
<table>
<tr>{dates}</tr>
<tr>{prices}</tr>
<tr>{discounts}</tr>
<tr>{types}</tr>
</table>
</div>
);
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

How to group by and sum an array of objects javascript?

const array = [
{
"data": {
"qty": "5",
"toy": {
"id": 3,
},
"available": "yes",
}
},
{
"data": {
"qty": "5",
"toy": {
"id": 10,
},
"available": "no"
}
},
{
"data": {
"qty": "59",
"toy": {
"id": 10,
},
"available": "yes",
}
},
{
"data": {
"qty": "5",
"toy": {
"id": 3,
},
"available": "yes",
}
}
]
var result = [];
array.reduce(function(res, value) {
if (!res['data']['toy'] || !res['data']['toy']['data']) {
res['data'] = {...value['data'] };
result.push(res['data'])
}
if (res['data']['available'] === value['data']['available'] && res['data']['toy']['id'] === value['data']['toy']['id']) {
res['data']['qty'] = parseInt(res['data']['qty']) + parseInt(value['data'].qty)
}
return res;
}, {'data': {}});
console.log(result)
I am working on a js project and I need a bit of help here. From the array, How to get a new array that has qty as the sum of the other qty value which data.toy.id and available same. i.e. I want the below array. My code is not working as excepted. Changes to the same or new code are also fine. Thank you.
const array = [
{
"data": {
"qty": "10",
"toy": {
"id": 3,
},
"available": "yes",
}
},
{
"data": {
"qty": "5",
"toy": {
"id": 10,
},
"available": "no"
}
},
{
"data": {
"qty": "59",
"toy": {
"id": 10,
},
"available": "yes",
}
}
]
You group the array into an object, where the keys are concatenation of available and id properties and finally transform the object back to an array using Object.values.
const
array = [
{ data: { qty: "5", toy: { id: 3 }, available: "yes" } },
{ data: { qty: "5", toy: { id: 10 }, available: "no" } },
{ data: { qty: "59", toy: { id: 10 }, available: "yes" } },
{ data: { qty: "5", toy: { id: 3 }, available: "yes" } },
],
result = Object.values(
array.reduce((r, { data }) => {
const k = data.available + data.toy.id;
if (r[k]) {
r[k].data.qty = String(Number(r[k].data.qty) + Number(data.qty));
} else {
r[k] = { data };
}
return r;
}, {})
);
console.log(result);
I'd suggest using Array.reduce() to group by a key, which will be combined value of the toy id and the available property.
We'd create a map of all toys based on this key, summing the quantity for each.
Finally, we'll use Object.values() to convert back into an array.
const array = [ { "data": { "qty": "5", "toy": { "id": 3, }, "available": "yes", } }, { "data": { "qty": "5", "toy": { "id": 10, }, "available": "no" } }, { "data": { "qty": "59", "toy": { "id": 10, }, "available": "yes", } }, { "data": { "qty": "5", "toy": { "id": 3, }, "available": "yes", } } ];
const result = Object.values(array.reduce((acc, { data: { qty, toy, available } }) => {
const key = `${toy.id}-${available}`;
acc[key] = acc[key] || { data: { qty: 0, toy, available } };
acc[key].data.qty += Number(qty);
return acc;
}, {}))
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
You can use Array#reduce() to create arrayHash object using as keys: ${c.data.toy.id}-${c.data.available}
Code:
const array = [{data: {qty: '5',toy: {id: 3,},available: 'yes',},},{data: {qty: '5',toy: {id: 10,},available: 'no',},},{data: {qty: '59',toy: {id: 10,},available: 'yes',},},{data: {qty: '5',toy: {id: 3,},available: 'yes',},},]
const arrayHash = array.reduce((a, { data }) => {
const key = `${data.toy.id}-${data.available}`
a[key] = a[key] || { data: { ...data, qty: 0 } }
a[key].data.qty = (+a[key].data.qty + +data.qty).toString();
return a
}, {})
const result = Object.values(arrayHash)
console.log(result)
I'd use just reduce
const a1 = [
{
"data": {
"qty": "5",
"toy": {
"id": 3,
},
"available": "yes",
}
},
{
"data": {
"qty": "5",
"toy": {
"id": 10,
},
"available": "no"
}
},
{
"data": {
"qty": "59",
"toy": {
"id": 10,
},
"available": "yes",
}
},
{
"data": {
"qty": "5",
"toy": {
"id": 3,
},
"available": "yes",
}
}
]
const a2 = a1.reduce((acc, it) => {
let found = acc.find(
dp => dp.data.toy.id === it.data.toy.id && dp.data.available === it.data.available
)
if(found){
found.data.qty = ( Number(found.data.qty) + Number(it.data.qty) ).toString()
}
else acc.push(it)
return acc
}, [])
console.log(JSON.stringify(a2, null,2))

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"));

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

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;
}, []));

How to calculate sum of count of array elements of multiple arrays by order and limit?

I have 3 arrays of 3 different types. Each array contains the count of an id (which might be duplicate like arrayOfB).
Each id has a limit value of count property is 10 (the count includes different types. Ex: if unique1 has 10 counts in type A, when process type B for unique1, it will be not processed).
const arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
];
const arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
The output will be like:
Map {
'UID1|A' => 10,
'UID2|A' => 10,
'UID4|A' => 1,
'UID3|B' => 5,
'UID4|C' => 6 }
I used a set to hold id, which already has the maximum count and map to hold the output.
const maxed = new Set();
const elements = new Map();
arrayOfA.forEach(element => {
if (element.count > 10) {
maxed.add(`${element.id}`);
elements.set(`${element.id}|${element.type}`, 10);
console.log(elements)
return;
}
if (elements.has(`${element.id}|${element.type}`)) {
const newCount = elements.get(`${element.id}|${element.type}`) + element.count;
newCount > 10 ? elements.set(`${element.id}|${element.type}`, 10) : elements.set(`${element.id}|${element.type}`, newCount);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
});
arrayOfB.forEach(element => {
if (maxed.has(`${element.id}`)) {
console.log(elements)
return;
}
const countOfA = elements.has(`${element.id}|A`) ? elements.get(`${element.id}|A`) : 0;
let newCount = countOfA + element.count;
if (elements.has(`${element.id}|${element.type}`)) {
newCount = newCount + element.get(`${element.id}|${element.type}`);
}
if (newCount > 10) {
maxed.add(`${element.id}`);
if ((10 - countOfA) > 0) elements.set(`${element.id}|${element.type}`, 10 - countOfA);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
})
arrayOfC.forEach(element => {
if (maxed.has(`${element.id}`)) {
console.log(elements)
return;
}
const countOfA = elements.has(`${element.id}|A`) ? elements.get(`${element.id}|A`) : 0
const countOfB = elements.has(`${element.id}|C`) ? elements.get(`${element.id}|C`) : 0
let newCount = countOfA + countOfB + element.count;
if (elements.has(`${element.id}|${element.type}`)) {
newCount = newCount + element.get(`${element.id}|${element.type}`);
}
if (newCount > 10) {
maxed.add(`${element.id}`);
if ((10 - countOfA - countOfB) > 0); elements.set(`${element.id}|${element.type}`, 10 - countOfA - countOfB);
console.log(elements)
return;
}
elements.set(`${element.id}|${element.type}`, element.count);
})
I want to ask about another faster implementation if any. I estimated my big O will be O(n) (n is the total length of 3 arrays). If elements of arrays do not contain the same id.
Edit:
Big thanks to you all, but seems like there's one edge case. The answers couldn't handle
var arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
{
"type": "B", "count": 1, "id": "UID3"
},
];
var arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
In arrayOfB, I have the UID3 occurs twice, so your answers doesn't seem to work on that case.
Instead of a Set for a maxed id, you could sum the count for every id and use it for all following arrays.
const
getKey = (...a) => a.join('|'),
rawData = [{ type: "A", count: 10, id: "UID1" }, { type: "A", count: 20, id: "UID2" }, { type: "A", count: 1, id: "UID4" }],
rawData3 = [{ type: "B", count: 5, id: "UID1" }, { type: "B", count: 5, id: "UID3" }],
rawData2 = [{ type: "C", count: 6, id: "UID1" }, { type: "C", count: 6, id: "UID4" }, { type: "C", count: 3, id: "UID2" }, { type: "C", count: 3, id: "UID3" }],
elements = new Map,
sums = new Map;
[rawData, rawData3, rawData2].forEach(a => a.forEach(({ type, count, id }) => {
var sum = sums.get(id) || 0,
key = getKey(id, type);
sums.set(id, sum + count);
if (sum >= 10) return;
if (sum + count > 10) {
if (10 - sum > 0) elements.set(key, 10 - sum);
return;
}
elements.set(key, count);
}));
[...elements].map(a => console.log(a.join(': ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Based on the assumption that you have missed to include "B" in your expected results set, two nested loops can provide the manipulation and filtering you want.
function getIdSummary(arrays) {
const maxValue = 10;
//Array of objects which we later conver to a map
//The aim is ease of indexing during the iterations
var summary = []
//A heler to find if a maxed uid is found in the summary
function isMaxed(uid) {
return summary.some(item => {
return item.uid === uid && item.count >= maxValue;
})
}
//Iterate all the arrays
arrays.forEach(anInputArray => {
//Iterate each array
anInputArray.forEach(item => {
if (!isMaxed(item.id)) {
summary.push({uid: item.id, type: item.type, count: item.count > maxValue ? 10 : item.count})
}
})
})
return new Map(summary.map(obj => {
return [obj.uid + '|' + obj.type, obj.count]
}))
}
var arrayOfA = [
{
"type": "A", "count": 10, "id": "UID1"
},
{
"type": "A", "count": 20, "id": "UID2"
},
{
"type": "A", "count": 1, "id": "UID4"
},
];
const arrayOfB = [
{
"type": "B", "count": 5, "id": "UID1"
},
{
"type": "B", "count": 5, "id": "UID3"
},
];
var arrayOfC = [
{
"type": "C", "count": 6, "id": "UID1"
},
{
"type": "C", "count": 6, "id": "UID4"
},
{
"type": "C", "count": 3, "id": "UID2"
},
{
"type": "C", "count": 3, "id": "UID3"
},
]
var m = getIdSummary([arrayOfA, arrayOfB, arrayOfC]);
console.log(Array.from(m));

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