I have this array of objects
test = [
{
'id': 1,
'name': 'XYZ'
'value': 10
'quantity': 100
},
{
'id': 1,
'name': 'XYZ'
'value': 20
'quantity': 200
},
{
'id': 2,
'name': 'ABC'
'value': 11
'quantity': 111
},
{
'id': 2,
'name': 'ABC'
'value': 22
'quantity': 222
}
]
And I want to group them by the id, but with the name and the {value, quantity} separated, like this:
result = {
1: [
'name': 'XYZ'
'items': [
{
'value': 10
'quantity': 100
},
{
'value': 20
'quantity': 200
}
]
],
2: [
'name': 'ABC'
'items': [
{
'value': 11
'quantity': 111
},
{
'value': 22
'quantity': 222
}
]
],
}
Any idea how I could do this? Grouping by the id I can do, but then I cannot extract the name.
Thanks
I think you can use reduce() in this case to group the elements by id
const test = [
{
'id': 1,
'name': 'XYZ',
'value': 10,
'quantity': 100,
},
{
'id': 1,
'name': 'XYZ',
'value': 20,
'quantity': 200,
},
{
'id': 2,
'name': 'ABC',
'value': 11,
'quantity': 111,
},
{
'id': 2,
'name': 'ABC',
'value': 22,
'quantity': 222,
}
];
const res = test.reduce((ac, {id, name, ...rest}) => ({...ac, [id]: ac[id] ? [...ac[id], rest] : [rest] }), {});
console.log(res)
You can easily achieve this result using reduce
const arr = [
{
id: 1,
name: "XYZ",
value: 10,
quantity: 100,
},
{
id: 1,
name: "XYZ",
value: 20,
quantity: 200,
},
{
id: 2,
name: "ABC",
value: 11,
quantity: 111,
},
{
id: 2,
name: "ABC",
value: 22,
quantity: 222,
},
];
const result = arr.reduce((acc, curr) => {
const { id, name, value, quantity } = curr;
if (acc[id]) {
acc[id].items.push({ value, quantity });
} else {
acc[id] = {
name,
items: [{ value, quantity }],
};
}
return acc;
}, {});
console.log(result);
You can use foreach in order to get what you need:
test = [
{
id: 1,
name: 'XYZ',
value: 10,
quantity: 100,
},
{
id: 1,
name: 'XYZ',
value: 20,
quantity: 200,
},
{
id: 2,
name: 'ABC',
value: 11,
quantity: 111,
},
{
id: 2,
name: 'ABC',
value: 22,
quantity: 222,
}
];
res = {};
test.forEach(el => {
let key = el.id;
if (!res.hasOwnProperty(key)) {
res[key] = {
name: el.name,
items: []
};
}
res[key].items.push({value: el.value, quantity: el.quantity});
});
console.log(res);
You could take a destructuring for id and name and get the rest of the object (Rest in Object Destructuring ...) as item.
Then assign a new object if not exists for the property id and push item to the items array of the group.
const
data = [{ id: 1, name: 'XYZ', value: 10, quantity: 100 }, { id: 1, name: 'XYZ', value: 20, quantity: 200 }, { id: 2, name: 'ABC', value: 11, quantity: 111 }, { id: 2, name: 'ABC', value: 22, quantity: 222 }],
result = data.reduce((r, { id, name, ...item }) => {
r[id] ??= { name, items: [] };
r[id].items.push(item);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have been having trouble understanding why temp is being affected here. When looking at the console when running this code:
const objectsArray = [
{'id': 1, 'name': 'Erik', 'yearsCompleted': 2, 'status': 'student'},
{'id': 2, 'name': 'Carol', 'yearsCompleted': 1, 'status': 'student'},
{'id': 3, 'name': 'Sarah', 'yearsCompleted': 4, 'status': 'student'}
];
temp = {
actions: objectsArray.map(student => {
if (student.yearsCompleted === 4) student.status = 'wow';
return student
})
}
console.log(temp)
temp2 = {
actions: objectsArray.map(student => {
if (student.yearsCompleted === 4) student.status = 'foo';
return student
})
}
console.log(temp)
console.log(temp2)
I end up getting the following result:
{ actions:
[ { id: 1, name: 'Erik', yearsCompleted: 2, status: 'student' },
{ id: 2, name: 'Carol', yearsCompleted: 1, status: 'student' },
{ id: 3, name: 'Sarah', yearsCompleted: 4, status: 'wow' } ] }
{ actions:
[ { id: 1, name: 'Erik', yearsCompleted: 2, status: 'student' },
{ id: 2, name: 'Carol', yearsCompleted: 1, status: 'student' },
{ id: 3, name: 'Sarah', yearsCompleted: 4, status: 'foo' } ] }
{ actions:
[ { id: 1, name: 'Erik', yearsCompleted: 2, status: 'student' },
{ id: 2, name: 'Carol', yearsCompleted: 1, status: 'student' },
{ id: 3, name: 'Sarah', yearsCompleted: 4, status: 'foo' } ] }
Why is temp in this case being affected? Are we getting a reference to the objectsArray when setting the actions property of temp to the objectsArray.map function? I thought temp wouldn't be affected in this case. Thank you.
That is because student.status = 'foo' changes the actual item in the temp array. You need to make a copy and return it using the spread operator(...) as below.
const objectsArray = [{
'id': 1,
'name': 'Erik',
'yearsCompleted': 2,
'status': 'student'
},
{
'id': 2,
'name': 'Carol',
'yearsCompleted': 1,
'status': 'student'
},
{
'id': 3,
'name': 'Sarah',
'yearsCompleted': 4,
'status': 'student'
}
];
temp = {
actions: objectsArray.map(student => {
if (student.yearsCompleted === 4) {
return { ...student,
status: 'wow'
}
};
return student
})
}
console.log(temp)
temp2 = {
actions: objectsArray.map(student => {
if (student.yearsCompleted === 4) {
return { ...student,
status: 'foo'
}
};
return student
})
}
console.log(temp)
console.log(temp2)
Given an array of categories and an array of entries, creates an array of objects with a category name and an entry count. Consider id is equal to categoryId.
var categories = [
{ name: 'Cats', id: 10 },
{ name: 'Dogs', id: 20 },
];
var entries = [
{categoryId: 10, name: 'Fluffy'},
{categoryId: 10, name: 'Spot'},
{categoryId: 10, name: 'Lil'},
{categoryId: 20, name: 'Tom'},
{categoryId: 20, name: 'Buck'},
{categoryId: 20, name: 'Flo'},
{categoryId: 20, name: 'Cheek'},
{categoryId: 10, name: 'Stan'},
{categoryId: 20, name: 'Stila'}
]
Expected Output: [{ name:'Cats', count: 4 }, { name:'Dogs', count: 5 }];
I wrote it like this below, but there seem to be a performance problem when you try to run it through hundreds of categories and tens of thousands of entries.
const categoriesByEntryCount = (categories, entries) =>
categories.map(category => ({
name: category.name,
count: entries.filter(entry => entry.categoryId === category.id).length,
}));
My question is there another way to write or implement this ?
You need to use Maps in all possible places.
var categories = new Map();
categories.set(10, 'Cats');
categories.set(20, 'Dogs');
var entries = [
{ categoryId: 10, name: 'Fluffy' },
{ categoryId: 10, name: 'Spot' },
{ categoryId: 10, name: 'Lil' },
{ categoryId: 20, name: 'Tom' },
{ categoryId: 20, name: 'Buck' },
{ categoryId: 20, name: 'Flo' },
{ categoryId: 20, name: 'Cheek' },
{ categoryId: 10, name: 'Stan' },
{ categoryId: 20, name: 'Stila' },
];
console.log(Array.from(
entries.reduce(
(m, { categoryId, name }) =>
m.set(categoryId, (m.get(categoryId) || 1) + 1),
new Map()
),
([k, v]) => ({ name: categories.get(k), count: v })
));
const categories = [ { name: 'Cats', id: 10 }, { name: 'Dogs', id: 20 } ];
const entries = [ { categoryId: 10, name: 'Fluffy' }, { categoryId: 10, name: 'Spot' }, { categoryId: 10, name: 'Lil' }, { categoryId: 20, name: 'Tom' }, { categoryId: 20, name: 'Buck' }, { categoryId: 20, name: 'Flo' }, { categoryId: 20, name: 'Cheek' }, { categoryId: 10, name: 'Stan' }, { categoryId: 20, name: 'Stila' } ];
// get number of occurences of each category in entries
const categoriesCount = entries.reduce((countMap, { categoryId }) =>
countMap.set( categoryId, 1 + (countMap.get(categoryId) || 0) )
, new Map);
// iterate over categories and return name and count in categoriesCount
const res = categories.map(({ name, id }) =>
({ name, count: categoriesCount.get(id) })
);
console.log(res);
We can do like below with time complexity O(M + N)
var categories = [
{ name: 'Cats', id: 10 },
{ name: 'Dogs', id: 20 },
];
var entries = [
{categoryId: 10, name: 'Fluffy'},
{categoryId: 10, name: 'Spot'},
{categoryId: 10, name: 'Lil'},
{categoryId: 20, name: 'Tom'},
{categoryId: 20, name: 'Buck'},
{categoryId: 20, name: 'Flo'},
{categoryId: 20, name: 'Cheek'},
{categoryId: 10, name: 'Stan'},
{categoryId: 20, name: 'Stila'}
]
const categoriesByEntryCount = (categories, entries) => {
const entriesHash = entries.reduce((acc, ele) => {
acc[ele.categoryId] = acc[ele.categoryId] ? acc[ele.categoryId] + 1 : 1;
return acc;
}, {});
return categories.map(category => ({
name: category.name,
count: entriesHash[category.id],
}));
}
console.log(categoriesByEntryCount(categories, entries))
This is obviously a reducing job.
var categories = [ { name: 'Cats', id: 10 }
, { name: 'Dogs', id: 20 }
],
entries = [ {categoryId: 10, name: 'Fluffy'}
, {categoryId: 10, name: 'Spot'}
, {categoryId: 10, name: 'Lil'}
, {categoryId: 20, name: 'Tom'}
, {categoryId: 20, name: 'Buck'}
, {categoryId: 20, name: 'Flo'}
, {categoryId: 20, name: 'Cheek'}
, {categoryId: 10, name: 'Stan'}
, {categoryId: 20, name: 'Stila'}
],
result = entries.reduce((cs,e) => ( cs.map(c => c.id === e.categoryId ? c.count ? c.count++
: c.count = 1
: c)
, cs
), categories);
console.log(result);
You may complain that the result includes the id property but that's just good.
I am looking for a way to modify array of objects like this:
[
{
id: 1,
name: 'xyz',
count: 3,
},
{
id: 2,
name: 'aaa',
count: 2,
},
{
id: 6,
name: 'bbb',
count: 1,
},
]
Now I want to map it shomehow to receive new array of objects without count properties but with duplicated objects by its count value. We will have:
[
{
id: 1,
name: 'xyz',
},
{
id: 1,
name: 'xyz',
},
{
id: 1,
name: 'xyz',
},
{
id: 2,
name: 'aaa',
},
{
id: 2,
name: 'aaa',
},
{
id: 6,
name: 'bbb',
},
]
I tried to do it with map and reduce but it didn't work out as expected...
You could use a nested mapping with an outer Array#flatMap.
var data = [{ id: 1, name: 'xyz', count: 3 }, { id: 2, name: 'aaa', count: 2 }, { id: 6, name: 'bbb', count: 1 }],
result = data.flatMap(({ count, ...o }) =>
Array.from({ length: count }, _ => ({ ... o })));
console.log(result);
Nina Scholz solution works fine, if you want something easier to read:
var data = [{
id: 1,
name: 'xyz',
count: 3,
},
{
id: 2,
name: 'aaa',
count: 2,
},
{
id: 6,
name: 'bbb',
count: 1,
},
];
var output = [];
for (var i = 0; i < data.length; i++) {
var element = data[i];
for (var j = 0; j < element.count; j++) {
output.push({
id: element.id,
name: element.name
});
}
}
console.log(output);
Please,
I have this JSON object and want to group values by type.
var costs = [
{ 'name': 'JON', 'flight':100, 'value': 12, type: 'uns' },
{ 'name': 'JON', 'flight':100, 'value': 35, type: 'sch' },
{ 'name': 'BILL', 'flight':200, 'value': 33, type: 'uns' },
{ 'name': 'BILL', 'flight':200, 'value': 45, type: 'sch' }
];
I want something like this:
var costs = [
{ 'name': 'JON', 'flight':100, 'uns': 12, 'sch': 35 },
{ 'name': 'BILL', 'flight':200, 'uns': 33, 'sch': 45}
];
I try use lodash but without sucess:
var compiled_costs = _.chain(costs)
.groupBy("flight")
.value();
{
"100":
[ {"name":"JON","flight":100,"value":12,"type":"uns"},
{"name":"JON","flight":100,"value":35,"type":"sch"}
],
"200":
[
{"name":"BILL","flight":200,"value":33,"type":"uns"},
{"name":"BILL","flight":200,"value":45,"type":"sch"}
]
}
var res = _.chain(costs)
.groupBy('flight') // group costs by flight
.mapValues(function(flightItems, flight) { // iterate flight arrays
return { // return obj on each flight array
name: _.get(flightItems, [0, 'name']), // just get name from first item of flight array
flight: flight,
uns: _.chain(flightItems) // get all flight items with type uns and sum items values
.filter({type: 'uns'})
.sumBy('value')
.value(),
sch: _.chain(flightItems)
.filter({type: 'sch'})
.sumBy('value')
.value()
}
})
.values() // get values from object
.value();
You can use native reduce() method:
const costs = [
{ 'name': 'JON', 'flight':100, 'value': 12, type: 'uns' },
{ 'name': 'JON', 'flight':100, 'value': 35, type: 'sch' },
{ 'name': 'BILL', 'flight':200, 'value': 33, type: 'uns' },
{ 'name': 'BILL', 'flight':200, 'value': 45, type: 'sch' }
];
const compiledCosts = costs.reduce((acc, { name, flight, value, type }) => {
let obj = acc.find(x => x.name === name);
if (typeof obj === 'undefined') {
acc.push({ name, flight, [type]: value });
} else {
obj[type] = value;
}
return acc;
}, []);
console.log(compiledCosts);
You could use a closure over a hash table for same named objects.
var data = [{ name: 'JON', flight: 100, value: 12, type: 'uns' }, { name: 'JON', flight: 100, value: 35, type: 'sch' }, { name: 'BILL', flight: 200, value: 33, type: 'uns' }, { name: 'BILL', flight: 200, value: 45, type: 'sch' }],
grouped = data.reduce(function (hash) {
return function (r, o) {
if (!hash[o.name]) {
hash[o.name] = { name: o.name, flight: o.flight };
r.push(hash[o.name]);
}
hash[o.name][o.type] = (hash[o.name][o.type] || 0) + o.value;
return r;
}
}(Object.create(null)), []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is what Map was made for. Since you are merging the data you might want to consider keeping the hashMap for quicker lookup. Otherwise turning it into an array is trivial as I demonstrate.
function compiled_costs(costs) {
var hash = new Map();
for (let cost of costs) {
if (hash.has(cost.name)) {
var values = hash.get(cost.name),
value = values.value,
type = values.type;
delete values.value;
delete values.type;
values.uns = value;
values.sch = cost.value;
} else hash.set(cost.name, cost)
}
return hash
}
var costs = [{
'name': 'JON',
'flight': 100,
'value': 12,
type: 'uns'
},
{
'name': 'JON',
'flight': 100,
'value': 35,
type: 'sch'
},
{
'name': 'BILL',
'flight': 200,
'value': 33,
type: 'uns'
},
{
'name': 'BILL',
'flight': 200,
'value': 45,
type: 'sch'
}
];
var formated = [...compiled_costs(costs).values()];
//formated
console.log('formated',formated);
//hashed
var hashed = [...compiled_costs(costs)];
console.log('hashed',hashed);
I have a problem with finding object in nested json! I need to do operations like 'add' to object and 'delete' object in that nested json. Would it be easy to get object by using "JSON.stringify" and in that string find objects ID parameter (every object has its own unique ID). Then from that point find its "wrapper" curly braces ({}) i could get object it self and then delete it or add new object in it.
I had this idea, but have no idea how to select its curly braces... I think it might work, but what do you thing? :)
Here would be the example object! https://jsfiddle.net/gb8hb8g7/
var aa = [
{name: "aaa",
id: 1,
items: [
{name: "bbb",
id: 15,
items: [
{name: "ccc",
id: 44},
{name: "ddd",
id: 91}
]},
{name: "eee",
id: 12}
]
}
];
console.log(JSON.stringify(aa));
You can traverse the nested JSON recursively, to perform the operations you need.
var aa = [
{name: "aaa",
id: 1,
items: [
{name: "bbb",
id: 15,
items: [
{name: "ccc",
id: 44},
{name: "ddd",
id: 91}
]},
{name: "eee",
id: 12}
]
}
];
var fff = {name: "fff", id: 13};
addObj(aa, 91, fff); // Add obj to same array as item 91
chgObj(aa, 91, '^', 'name', 'zzz'); // Change 'name' property of item 91
chgObj(aa, 91, '+', 'other', 'test'); // Add property to item 91
chgObj(aa, 91, '+', 'gone', 'delete me'); // Add property to item 91
chgObj(aa, 91, '-', 'gone'); // Delete property from item 91
dltObj(aa, 44); // Delete item 44
function addObj(itemArr, nId, newObj) {
for (var i = 0; i < itemArr.length; i++) {
if (itemArr[i].id && itemArr[i].id === nId) {
itemArr.push(newObj);
} else {
if (itemArr[i].items) {
addObj(itemArr[i].items, nId, newObj);
}
}
}
}
function chgObj(itemArr, nId, operator, prop, val) {
for (var i = 0; i < itemArr.length; i++) {
if (itemArr[i].id && itemArr[i].id === nId) {
switch (operator) {
case '+':
if (!itemArr[i][prop]) {
itemArr[i][prop] = val;
}
break;
case '-':
if (itemArr[i][prop]) {
delete itemArr[i][prop];
}
break;
case '^':
if (itemArr[i][prop]) {
itemArr[i][prop] = val;
}
break;
}
} else {
if (itemArr[i].items) {
chgObj(itemArr[i].items, nId, operator, prop, val);
}
}
}
}
function dltObj(itemArr, nId) {
for (var i = 0; i < itemArr.length; i++) {
if (itemArr[i].id && itemArr[i].id === nId) {
itemArr.splice(i, 1);
} else {
if (itemArr[i].items) {
dltObj(itemArr[i].items, nId);
}
}
}
}
alert(JSON.stringify(aa));
new fiddle: https://jsfiddle.net/ta4pjqew/2
You should be able to just use your objects like you are traversing a big array:
var aa = [
{name: "aaa",
id: 1,
items: [
{name: "bbb",
id: 15,
items: [
{name: "ccc",
id: 44},
{name: "ddd",
id: 91}
]},
{name: "eee",
id: 12}
]
}
];
aa[0].name = 'abc';
aa[0].newprop = 23;
console.log(aa[0].items[0].items[1]);
delete aa[0].items[0].items[1];
console.log(aa[0].items[0].items[1]);
console.log(JSON.stringify(aa));
Take a look at object-scan. Makes it every easy to write clean and maintainable code to modify complex data structures. Here is how one could answer your question.
// const objectScan = require('object-scan');
const tool = (() => {
const scanner = objectScan(['**.items[*]'], {
abort: true,
rtn: 'bool',
filterFn: ({ value, parent, property, context }) => {
if (value.id === context.id) {
context.fn({ value, parent, property });
return true;
}
return false;
}
});
return {
add: (data, id, obj) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property + 1, 0, obj) }),
del: (data, id) => scanner(data, { id, fn: ({ parent, property }) => parent.splice(property, 1) }),
mod: (data, id, prop, v = undefined) => scanner(data, {
id,
fn: ({ value }) => {
if (value !== undefined) {
value[prop] = v;
} else {
delete value[prop];
}
}
})
};
})();
// -------------------------------
const aa = [{ name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'ddd', id: 91 } ] }, { name: 'eee', id: 12 } ] }];
const fff = { name: 'fff', id: 13 };
const exec = (fn) => {
console.log('---------------');
console.log(fn.toString());
console.log(fn());
console.log(aa);
};
exec(() => tool.add(aa, 91, fff)); // Add obj to array after item 91
exec(() => tool.mod(aa, 91, 'name', 'zzz')); // Change 'name' property of item 91
exec(() => tool.mod(aa, 91, 'other', 'test')); // Add property to item 91
exec(() => tool.mod(aa, 91, 'gone', 'delete me')); // Add property to item 91
exec(() => tool.mod(aa, 91, 'gone')); // Delete property from item 91
exec(() => tool.del(aa, 44)); // Delete item 44
// => ---------------
// => () => tool.add(aa, 91, fff)
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'ddd', id: 91 }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
// => ---------------
// => () => tool.mod(aa, 91, 'name', 'zzz')
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'zzz', id: 91 }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
// => ---------------
// => () => tool.mod(aa, 91, 'other', 'test')
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'zzz', id: 91, other: 'test' }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
// => ---------------
// => () => tool.mod(aa, 91, 'gone', 'delete me')
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'zzz', id: 91, other: 'test', gone: 'delete me' }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
// => ---------------
// => () => tool.mod(aa, 91, 'gone')
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'ccc', id: 44 }, { name: 'zzz', id: 91, other: 'test', gone: undefined }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
// => ---------------
// => () => tool.del(aa, 44)
// => true
// => [ { name: 'aaa', id: 1, items: [ { name: 'bbb', id: 15, items: [ { name: 'zzz', id: 91, other: 'test', gone: undefined }, { name: 'fff', id: 13 } ] }, { name: 'eee', id: 12 } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan