Turning array of objects into an organized object - javascript - javascript

I'm trying to manipulate this object so it matches the corresponding count to its id.
const data= [
[
{id: "333", count: 555},
{id: "2", count: 133},
{id: "444", count: 13},
{id: "433", count: 32},
{id: "3333", count: 2}
],
[
{id: "333", count: 5565},
{id: "2", count: 1633},
{id: "444", count: 136},
{id: "433", count: 362},
{id: "3333", count: 62}
],
[
{id: "333", count: 585},
{id: "2", count: 1383},
{id: "444", count: 138},
{id: "433", count: 328},
{id: "3333", count: 82}
],
]
To this:
const newData = [
{ id: "333", count: [555, 5565, 585] },
{ id: "2", count: [133, 1633, 1383] },
{ id: "444", count: [13, 136, 138] },
{ id: "433", count: [32, 362, 328] },
{ id: "3333", count: [2, 62, 82] }
]
Something I tried was this, but this just gave me an array with each count seperate.
let ob = test.reduce( ( acc, e, i) => {
let obj = {}
e.forEach((value, index) => {
obj[value.recommendationItemIdExternal] = [value.recommendationItemRating];
})
acc.push(obj);
return acc
}, [] );

You can easily achieve this result using Map and reduce
const data = [
[
{ id: "333", count: 555 },
{ id: "2", count: 133 },
{ id: "444", count: 13 },
{ id: "433", count: 32 },
{ id: "3333", count: 2 },
],
[
{ id: "333", count: 5565 },
{ id: "2", count: 1633 },
{ id: "444", count: 136 },
{ id: "433", count: 362 },
{ id: "3333", count: 62 },
],
[
{ id: "333", count: 585 },
{ id: "2", count: 1383 },
{ id: "444", count: 138 },
{ id: "433", count: 328 },
{ id: "3333", count: 82 },
],
];
const map = new Map(),
newData = [];
const dict = data.flat().reduce((acc, curr) => {
!acc.has(curr.id)
? map.set(curr.id, [curr.count])
: map.get(curr.id).push(curr.count);
return acc;
}, map);
for (let [id, count] of dict) {
newData.push({ id, count });
}
console.log(newData);

A nested for loop will do it. Just iterate through the data and store the count (or b) value in another object, using the id as the key.
let adjusted = {};
data.forEach(e => {
for (let item of e) {
if (adjusted[item.id] == undefined)
adjusted[item.id] = [];
if(item.count != undefined)
adjusted[item.id].push(item.count);
else if(item.b != undefined)
adjusted[item.id].push(item.b);
}
});
console.log(adjusted);
const data = [
[{
id: "333",
count: 555
},
{
id: "2",
count: 133
},
{
id: "444",
count: 13
},
{
id: "433",
count: 32
},
{
id: "3333",
count: 2
},
],
[{
id: "333",
count: 5565
},
{
id: "2",
count: 1633
},
{
id: "444",
count: 136
},
{
id: "433",
count: 362
},
{
id: "3333",
count: 62
},
],
[{
id: "333",
b: 585
},
{
id: "2",
b: 1383
},
{
id: "444",
b: 138
},
{
id: "433",
b: 328
},
{
id: "3333",
b: 82
},
],
]
let adjusted = {};
data.forEach(e => {
for (let item of e) {
if (adjusted[item.id] == undefined)
adjusted[item.id] = [];
if(item.count != undefined)
adjusted[item.id].push(item.count);
else if(item.b != undefined)
adjusted[item.id].push(item.b);
}
});
console.log(adjusted);

You can use a combination of .reduce() and .map() array methods as in the demo below.
const newArray = data.reduce((acc, cur) => cur.map((o, i) => ({
id: o.id,
count: [...(acc[i] && acc[i].count || []), o.count]
})), []);
Please note that this only works if each element of the original array has the ids in the same order, otherwise a little more processing is required.
DEMO
const data= [
[
{id: "333", count: 555},
{id: "2", count: 133},
{id: "444", count: 13},
{id: "433", count: 32},
{id: "3333", count: 2}
],
[
{id: "333", count: 5565},
{id: "2", count: 1633},
{id: "444", count: 136},
{id: "433", count: 362},
{id: "3333", count: 62}
],
[
{id: "333", count: 585},
{id: "2", count: 1383},
{id: "444", count: 138},
{id: "433", count: 328},
{id: "3333", count: 82}
],
];
const newArray = data.reduce((acc,cur) => cur.map((o,i) => ({id:o.id,count:[...(acc[i] && acc[i].count || []),o.count]})),[]);
console.log( newArray );

Related

How to create a nested array of object from an array of objects

How Can I loop through this array of objects and change it so that the individual menu items are nested in the object menu_name?
const menus = [
{ menu_name: 'Entre', id:0 },
{
name: 'Soup',
price: 14.99,
id:1
},
{
name: 'Chips & Salsa',
price: 7.99,
id:2
},
{
name: 'Chicken Nuggets',
price: 12.99,
id:3
},
{ menu_name: 'Sides', id:4 },
{
name: 'Fries',
price: 4.99,
id:5
},
{
name: 'Drinks',
price: 2.99,
id:6
},
{
name: 'Onion Rings',
price: 5.99,
id:7
},
];
the end result should look like this for each menu_name object, where an array of menus is nested in the menu_name object
{
menu_name: 'Sides',
menu: [
{
name: 'Fries',
price: 4.99,
},
{
name: 'Drinks',
price: 2.99,
},
{
name: 'Onion Rings',
price: 5.99,
},
],
},
You can easily achieve this using reduce and object destructuring
const menus = [
{ menu_name: "Entre", id: 0 },
{
name: "Soup",
price: 14.99,
id: 1,
},
{
name: "Chips & Salsa",
price: 7.99,
id: 2,
},
{
name: "Chicken Nuggets",
price: 12.99,
id: 3,
},
{ menu_name: "Sides", id: 4 },
{
name: "Fries",
price: 4.99,
id: 5,
},
{
name: "Drinks",
price: 2.99,
id: 6,
},
{
name: "Onion Rings",
price: 5.99,
id: 7,
},
];
const result = menus.reduce((acc, curr) => {
const { menu_name } = curr;
if (menu_name) {
acc.push({ menu_name, menu: [] });
} else {
const { name, price } = curr;
acc[acc.length - 1].menu.push({ name, price });
}
return acc;
}, []);
console.log(result);
var newMenu = [];
menus.forEach(menu=>{
if(menu.menu_name){
newMenu.push({...menu, menu: []})
}else{
newMenu[newMenu.length-1].menu.push(menu)
}
});

How does one order array-items of complex data-structures by a specific but deeply nested property-value?

This is my array, I have an object and then a count of how many repeats Id have for example the first object has the Id 2 repeated 3 times.
[{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]
Now I need to order this array based on an Id for example the number "9" so if the first object has the Maximus count of the id 9 of all it will be the first and the others whit minus count would be bellow, like this, the number 9 will be a random number.
[{
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}]
Using Array.prototype.sort one needs to write a function which compares two array/list items according to the OP's requirements.
Such a comparator is expected to return a number value either grater than Zero or lower than Zero or Zero itself in case of item equality.
Thus one needs to find two different counts, one count for each item which will be found by searching an item's Category array by an additionally provided id value.
In order to keep the compare function reusable it is implemented as a function which allows a context to be bound to it which in the OP's case is an object that features the id one is looking for ... e.g. something like ... { id: 9 } or { id: 4 } ...
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
const bCount = b.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
As one of the comments does point out, the above code requires a version of at least node 14.0.0 due to the function compareByBoundIdCountOfItemCategoryList which uses both the Optional Chaining Operator / ?. and the Nullish Coalescing Operator / ??.
In order to let the script not break one has to replace the line ...
... aCount = a.Category.find(ctgry => ctgry.Id === id)?.count ?? -1;
... with this alternative ...
... aCount = (a.Category.find(ctgry => ctgry.Id === id) || { count: -1 }).count;
function compareByBoundIdCountOfItemCategoryList(a, b) {
const { id } = this;
const aCount = (
a.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
const bCount = (
b.Category.find(ctgry => ctgry.Id === id) ||
{ count: -1 }
).count;
// in case of equal counts compare the `Category` array's lengths'.
return (bCount - aCount) || (b.Category.length - a.Category.length);
}
const sampleList = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
console.log(
'{ id: 9 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 9 }))
);
console.log(
'{ id: 4 } ...',
sampleList
.sort(compareByBoundIdCountOfItemCategoryList.bind({ id: 4 }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I find this format a simpler alternative to the answer from Peter Seliger. We simply store the sought id in a closure when creating the comparator function we pass to sort:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) ?.count ?? -1) -
(c1 .find (({Id}) => Id === categoryId) ?.count ?? -1)
const input = [{Id: 1, Info: "Info", Category: [{Id: 2, count: 3}, {Id: 4, count: 1}, {Id: 8, count: 1}, {Id: 18, count: 1}, {Id: 9, count: 1}, {Id: 3, count: 1}]}, {Id: 2, Info: "Info 2", Category: [{Id: 2, count: 3}, {Id: 9, count: 2}, {Id: 21, count: 1}, {Id: 3, count: 1}]}, {Id: 3, Info: "Info 3", Category: [{Id: 4, count: 1}, {Id: 11, count: 1}, {Id: 9, count: 1}]}]
console .log (input .sort (byCategoryCount (9)))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you don't have the nullish coalescing operator available in your environment, this variant is not much worse:
const byCategoryCount = (categoryId) => ({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}) .count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}) .count
We could also choose to write a wrapper function that returns a sorted version without mutating the original list. It might look like this:
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (byCategoryCount (categoryId))
But at that point we might start to wonder whether the helper function is offering us anything and we might choose to refactor to
const sortByCategoryCount = (categoryId, xs) =>
[... xs] .sort (({Category: c1}, {Category: c2}) =>
(c2 .find (({Id}) => Id === categoryId) || {count: -1}).count -
(c1 .find (({Id}) => Id === categoryId) || {count: -1}).count
)
This should work for you sortByCount:
var my_arr = [{
Id: 1,
Info: "Info",
Category: [
{ Id: 2, count: 3 },
{ Id: 4, count: 1 },
{ Id: 8, count: 1 },
{ Id: 18, count: 1 },
{ Id: 9, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 2,
Info: "Info 2",
Category: [
{ Id: 2, count: 3 },
{ Id: 9, count: 2 },
{ Id: 21, count: 1 },
{ Id: 3, count: 1 },
],
}, {
Id: 3,
Info: "Info 3",
Category: [
{ Id: 4, count: 1 },
{ Id: 11, count: 1 },
{ Id: 9, count: 1 },
],
}];
function sortByCount(arr, targetId){
var arr_temp = [];
arr.forEach(el => {
var elem = el.Category.filter(e => e.Id === targetId)[0];
var value = elem ? elem.count : -1;
arr_temp.push({
value: value,
obj: el
});
});
arr_temp.sort((a,b)=> b.value - a.value);
return arr_temp.map(el => el.obj);
}
var sortedArr = sortByCount(my_arr, 9);
console.log(sortedArr)

How to first combine properties of an object then remove the duplicates in an array of objects using Javascript

I have an array of objects here:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
I want to add quantities of the duplicate objects together before removing them
So the result is:
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test3", quantity:2 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:2 },
{ id: 6, name: "test8", quantity:1 }
];
I have seen variations of it done removing duplicates using map or reduce but I haven't seen anything that can what I want to accomplish in an eloquent way without using too many loops.
I have been thinking about how to best accomplish this all day and haven't found anything, any help would be appreciated
You can use reduce with an object to store the element with each id.
const arr = [
{ id: 1, name: "test1", quantity:1 },
{ id: 2, name: "test2", quantity:1 },
{ id: 2, name: "test3", quantity:1 },
{ id: 3, name: "test4", quantity:1 },
{ id: 4, name: "test5", quantity:1 },
{ id: 5, name: "test6", quantity:1 },
{ id: 5, name: "test7", quantity:1 },
{ id: 6, name: "test8", quantity:1 }
];
const res = Object.values(
arr.reduce((acc,curr)=>{
acc[curr.id] = acc[curr.id] || {...curr, quantity: 0};
acc[curr.id].quantity += curr.quantity;
return acc;
}, {})
);
console.log(res);
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 }
];
var result = arr.reduce(function (r, a) {
r[a.id] = r[a.id] || { id: a.id, quantity: 0, name: a.name };
r[a.id].quantity += a.quantity;
return r;
}, Object.create(null));
console.log(JSON.stringify(result));
Using forEach loop and build object with aggregated quantity count.
const convert = (arr) => {
const res = {};
arr.forEach(({ id, ...rest }) =>
res[id] ? (res[id].quantity += 1) : (res[id] = { id, ...rest })
);
return Object.values(res);
};
const arr = [
{ id: 1, name: "test1", quantity: 1 },
{ id: 2, name: "test2", quantity: 1 },
{ id: 2, name: "test3", quantity: 1 },
{ id: 3, name: "test4", quantity: 1 },
{ id: 4, name: "test5", quantity: 1 },
{ id: 5, name: "test6", quantity: 1 },
{ id: 5, name: "test7", quantity: 1 },
{ id: 6, name: "test8", quantity: 1 },
];
console.log(convert(arr));

Count duplicates and order them in array of objects Javascript

I have the following array of objects:
[
{
id: 1,
someOtherStuff: 'abc,
Drink: { name: 'Coca-Cola', price: 2.5 }
},
{
id: 2,
someOtherStuff: 'def,
Drink: { name: 'Fanta Orange', price: 3 }
},
{
id: 3,
someOtherStuff: 'ghi,
Drink: { name: 'Sprite', price: 1.8 }
},
{
id: 6,
someOtherStuff: 'jkl,
Drink: { name: 'Coca-Cola', price: 2.5 }
},
{
id: 7,
someOtherStuff: 'mno,
Drink: { name: 'Coca-Cola', price: 2.5 }
}
]
i want to group them by duplicates like this:
[
{
count: 3,
drinkName: 'Coca-Cola',
price: 2.5
},
{
count: 1,
drinkName: 'Fanta Orange',
price: 3
},
{
count: 1,
drinkName: 'Sprite',
price: 1.8
}
]
I have tried in several ways to map a new array.But unfortunately I can't get it to work
I hope someone can help me with that
let result= {};
inputArray.map((item)=>{
let key = `${item.Drink.name}-${item.Drink.price}`;
if(result[key]){
result[key].count = result[key].count +1;
}else{
result[key] = {count:1,drinkName:item.Drink.name,price:item.Drink.price};
}
});
result = Object.values(result);

Using nested reduces on an object by properties

I have data like this:
const data = [
{ id: 1, cat: "human", age: "10" },
{ id: 2, cat: "human", age: "20" },
{ id: 3, cat: "human", age: "10" },
{ id: 4, cat: "animal", age: "10" },
{ id: 5, cat: "animal", age: "20" },
{ id: 6, cat: "animal", age: "10" },
{ id: 7, cat: "alien", age: "10" },
{ id: 8, cat: "alien", age: "20" },
{ id: 9, cat: "alien", age: "10" },
];
I want to group this data something like that:
const gr = {
human: {
all: [
{ id: 1, cat: "human", age: "10" },
{ id: 2, cat: "human", age: "20" },
{ id: 3, cat: "human", age: "10" },
],
ages: {
"10": [
{ id: 1, cat: "human", age: "10" },
{ id: 3, cat: "human", age: "10" },
],
"20": [
{ id: 2, cat: "human", age: "20" },
],
}
},
animal: {...},
alien: {...},
}
I do first reduce like that:
const gr = data.reduce((acc, el) => {
const { cat } = el;
acc[cat] = acc[cat] || { all: [] };
acc[cat].all.push(el);
return acc;
}, {});
But I can't make a nested reduce here. I can do it separately like that:
const grAge = gr.human.all.reduce((acc,el) => {
const {age} = el;
acc[age] = acc[age] || [];
acc[age].push(el);
return acc;
},{});
gr.human["ages"] = grAge;
But obviously, this is not so efficient and needs more work. Maybe like this:
Object.keys(gr).forEach(key => {
const grAge = gr[key].all.reduce((acc,el) => {
const {age} = el;
acc[age] = acc[age] || [];
acc[age].push(el);
return acc;
},{});
gr[key]["ages"] = grAge;
});
Can I join those reduces in a single step?
If there are any other good methods I can use them, I don't need to use the reduce method.
You could take a sinle loop approach and assign the wanted structure to either allor to a nested strcture.
If you like to get a more dynamic version, you need to simplify the result structure for every nesting level (this means, the age level would contain an all property).
const
data = [{ id: 1, cat: "human", age: "10" }, { id: 2, cat: "human", age: "20" }, { id: 3, cat: "human", age: "10" }, { id: 4, cat: "animal", age: "10" }, { id: 5, cat: "animal", age: "20" }, { id: 6, cat: "animal", age: "10" }, { id: 7, cat: "alien", age: "10" }, { id: 8, cat: "alien", age: "20" }, { id: 9, cat: "alien", age: "10" }],
result = data.reduce((r, o) => {
r[o.cat] = r[o.cat] || { all: [], ages: {} };
r[o.cat].all.push(o);
r[o.cat].ages[o.age] = r[o.cat].ages[o.age] || [];
r[o.cat].ages[o.age].push(o);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another way to do this would be to get every unique category and ages using Sets, and then reducing them into your final JSON :
EDIT : It seems like the Stack Overflow snippet doesn't like it, but executing it in your browser console will give out the correct result
const data = [
{ id: 1, cat: "human", age: "10" },
{ id: 2, cat: "human", age: "20" },
{ id: 3, cat: "human", age: "10" },
{ id: 4, cat: "animal", age: "10" },
{ id: 5, cat: "animal", age: "20" },
{ id: 6, cat: "animal", age: "10" },
{ id: 7, cat: "alien", age: "10" },
{ id: 8, cat: "alien", age: "20" },
{ id: 9, cat: "alien", age: "10" },
];
const output = [...new Set(data.map(thing => thing.cat))].reduce((acc, category) => {
const catData = data.filter(thing => thing.cat === category)
return {
[category]: {
all: catData,
ages : [...new Set(catData.map(catThing => catThing.age))].reduce((catAcc, age) => ({
[age]: [...catData.filter(catThing => catThing.age === age)],
...catAcc
}), {})
},
...acc
}
}, {})
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories