Instead of explaining the problem in words, I've just made a quick visual representation below.
Say I have the following array:
let arr1 = [
{
id: 1,
someKey: someValue
},
{
id: 2,
someKey: someValue
},
]
and another array:
let arr2 = [
{
id: 1,
numberOfItems: 10
},
{
id: 2,
numberOfItems: 20
},
]
How would I create the following array?
let result = [
{
id: 1,
someKey: someValue,
numberOfItems: 10
},
{
id: 2,
someKey: someValue,
numberOfItems: 10
},
]
So as you can see, both arrays have the same id value. I want to take numberOfItems: 10 from the second array and place it into the first array under the same id.
Note: the two ids are completely different, have different properties and length. The only similarity is the id
You could first create a map with id as key and then combine the objects This would solve the problem in O(n):
let arr1 = [
{
id: 1,
someKey: 3
},
{
id: 2,
someKey: 6
},
];
let arr2 = [
{
id: 1,
numberOfItems: 10
},
{
id: 2,
numberOfItems: 20
},
];
let arr2Map = arr2.reduce((acc, curr) => {
acc[curr.id] = curr
return acc;
}, {});
let combined = arr1.map(d => Object.assign(d, arr2Map[d.id]));
console.log(combined);
let arr1 = [
{
id: 1,
someKey: 3
},
{
id: 2,
someKey: 6
},
];
let arr2 = [
{
id: 1,
numberOfItems: 10
},
{
id: 2,
numberOfItems: 20
},
];
let idToNumberOfItem = {};
arr2.forEach(({id, numberOfItems})=> idToNumberOfItem[id]=numberOfItems)
arr1.forEach((item)=> item['numberOfItems'] = idToNumberOfItem[item.id])
console.log(arr1);
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 months ago.
Improve this question
there are two arrays of objects,
const students = [
{ student_id: 1, name: "Arthur" },
{ student_id: 2, name: "Peter" },
{ student_id: 3, name: "Molly" },
];
const student_objects = [
{ student_id: 1, object: "pen" },
{ student_id: 2, object: "pen" },
{ student_id: 3, object: "book" },
{ student_id: 1, object: "book" },
{ student_id: 3, object: "phone" },
];
the expected output :
[
{ student_id: 1, name: 'Arthur', object: [ 'pen', 'book' ] },
{ student_id: 2, name: 'Peter', object: [ 'pen' ] },
{ student_id: 3, name: 'Molly', object: [ 'book', 'phone' ] }
]
I tried two ways , the first one is :
function practice(students, student_objects) {
students.forEach((ele) => (ele.object = []));
for (let i = 0; i < students.length; i++) {
for (let j = 0; j < student_objects.length; j++) {
if (student_objects[j].student_id === students[i].student_id) {
students[i].object.push(student_objects[j].object);
}
}
}
return students;
}
and the second one is :
function practice(students, student_objects) {
const student_objects_new = student_objects.reduce((obj, item) => {
obj[item.student_id] = obj[item.student_id] || [];
obj[item.student_id].push(item.object);
return obj;
}, {});
const answer = students.map((element) => ({
...element,
object: [...student_objects_new[element.student_id]],
}));
return answer;
}
both of the ways are not efficient , is there better ways to achieve the expected output?
You could collect the objects first and then map the students with their objects.
Big O: O(n + m)
const
students = [{ student_id: 1, name: "Arthur" }, { student_id: 2, name: "Peter" }, { student_id: 3, name: "Molly" }],
student_objects = [{ student_id: 1, object: "pen" }, { student_id: 2, object: "pen" }, { student_id: 3, object: "book" }, { student_id: 1, object: "book" }, { student_id: 3, object: "phone" }],
relations = student_objects.reduce((r, { student_id, object }) => {
(r[student_id] ??= []).push(object);
return r;
}, {}),
result = students.map(o => ({ ...o, objects: relations[o.student_id] || [] }));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can try to use reduce to reorganize the student_objects array.
{"1":["pen","book"],"2":["pen"],"3":["book","phone"]}
The above is the reorganized data format.
Then when calling map, use the spread syntax to copy the attributes, and add an array of objects based on student_id from the restructured data.
The reason for not reorganizing student_objects in the map.
It is to avoid reorganizing the data every time and wasting resources.
Below is an example of each step :
const students = [
{ student_id: 1, name: "Arthur" },
{ student_id: 2, name: "Peter" },
{ student_id: 3, name: "Molly" }
];
const student_objects = [
{ student_id: 1, object: "pen" },
{ student_id: 2, object: "pen" },
{ student_id: 3, object: "book" },
{ student_id: 1, object: "book" },
{ student_id: 3, object: "phone" }
];
const restructure = arr =>
arr.reduce((accumu, {student_id, object}) => {
accumu[student_id] = [...accumu[student_id] ?? '', object];
return accumu;
}, {});
const reorganized = restructure(student_objects);
const result = students.map(student => ({
...student,
object: reorganized[student.student_id]
}));
result.forEach(r => console.log(JSON.stringify(r)));
Integrate steps and wrap into a function :
const students = [
{ student_id: 1, name: "Arthur" },
{ student_id: 2, name: "Peter" },
{ student_id: 3, name: "Molly" }
];
const student_objects = [
{ student_id: 1, object: "pen" },
{ student_id: 2, object: "pen" },
{ student_id: 3, object: "book" },
{ student_id: 1, object: "book" },
{ student_id: 3, object: "phone" }
];
const result = (students, student_objects) => {
const reorganized =
student_objects.reduce((accumu, {student_id, object}) => {
accumu[student_id] = [...accumu[student_id] ?? '', object];
return accumu;
}, {});
return students.map(student => ({
...student,
object: reorganized[student.student_id]
}));
}
result(students, student_objects)
.forEach(r => console.log(JSON.stringify(r)));
I have an array of arrays, which contain objects, would like to get the value of a certain key and return it as a big array, have tried a nested map but it returns multiple array's rather than a single array.
const items = [
{
id: 1,
sub_items: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
},
{
id: 2,
sub_items: [
{
id: 4
},
{
id: 5
},
{
id: 6
}
]
}
]
const subItemIDs = items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
)
console.log(subItemIDs);
Expected output
[1, 2, 3, 4, 5, 6]
Actual output
[ [1,2,3], [4,5,6] ]
You can use arrays.flat(). I can provide more specific code once output is mentioned in the question
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]
You could take Array#flatMap to get a flat array from nested arrays.
const
items = [{ id: 1, sub_items: [{ id: 1 }, { id: 2 }, { id: 3 }] }, { id: 2, sub_items: [{ id: 4 }, { id: 5 }, { id: 6 }] }],
subItemIDs = items.flatMap(({ sub_items }) => sub_items.map(({ id }) => id));
console.log(subItemIDs);
Achieved this with:
const items = [
{
id: 1,
sub_items: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
},
{
id: 2,
sub_items: [
{
id: 4
},
{
id: 5
},
{
id: 6
}
]
}
]
const subItemIDs = [].concat(...items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
))
console.log(subItemIDs);
Sometimes, the obvious is the easiest:
Given a data structure that looks like this
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
A trivial function like this
function extract_item_ids( items ) {
const ids = [];
for ( const item of items ) {
for ( const {id} of sub_items ) {
ids.push(id);
}
}
return ids;
}
should do the trick. If you want to collect the ids from a tree of any depth, it's just as easy:
function extract_item_ids( items ) {
const ids = [];
const pending = items;
while ( pending.length > 0 ) {
const item = pending.pop();
ids.push(item.id);
pending.push(...( item.sub_items || [] ) );
}
return ids;
}
And collecting the set of discrete item IDs is no more difficult:
If you want to collect the ids from a tree of any depth, it's just as easy:
function extract_item_ids( items ) {
const ids = new Set();
const pending = [...items];
while ( pending.length > 0 ) {
const item = pending.pop();
ids.add(item.id);
pending.push(...( item.sub_items || [] ) );
}
return Array.from(ids);
}
As is the case with most things JavaScript, you have several options. Some are more efficient than others, others have a certain stylistic purity, others might better speak to your fancy. Here are a few:
Array.flat
With array flat you can take your original code and have the JS Engine flatten the array down to a one-dimensional array. Simply append .flat() onto the end of your map.
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
const subItemIds = items.map( (item) =>
item.sub_items.map( (subItem) => subItem.id )
).flat()
console.log(subItemIds);
Array.reduce
Another method is to use reduce to iterate over the object and build an accumulation array using Array.reduce. In the example below, when pushing onto the array, the spread operator (...) is used to break the array into elements.
const items = [
{ id: 1, sub_items: [ { id: 1 }, { id: 2 }, { id: 3 }, ] },
{ id: 2, sub_items: [ { id: 4 }, { id: 5 }, { id: 6 }, ] },
];
const subItemIds = items.reduce((arr,item) => (
arr.push(...item.sub_items.map((subItem) => subItem.id)), arr
),[])
console.log(subItemIds);
Other
Other answers here make use of custom functions or Array.flatMap, which should be explored as they could lead to more readable and efficient code, depending on the program's needs.
I compare id with two array with object.
Here is my function:
array1 = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
];
array2 = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
];
const compareFunction = (array1, array2) => {
array2.map((allValue) => {
array1.map((value) => {
allValue.selected = value.id === allValue.id;
});
})
return array2;
}
I think I will get the array2 like
[{ id: 1, selected: true }, { id: 2, selected: true },{ id: 3, selected: true }]
but actually array2 become
[{ id: 1, selected: false }, { id: 2, selected: false },{ id: 3, selected: true }]
Only the last array argument selected become true.
Which step was wrong ? Thanks.
Convert the 2nd array to a Set of id values. Iterate the 1st array with a Array.map() and create a new object for each item, by spreading the current object, and adding the selected value. To get the selected value check if the Set contains that current item id.
const array1 = [{ id: 1 },{ id: 2 },{ id: 3 }];
const array2 = [{ id: 1 },{ id: 2 },{ id: 3 }];
const a2Set = new Set(array2.map(o => o.id))
const result = array1.map(o => ({ ...o, selected: a2Set.has(o.id) }))
console.log(result)
checkout this :
array1 = [{ id: 1 },{ id: 2 },{ id: 3 }];
array2 = [{ id: 1 },{ id: 2 },{ id: 3 }];
const compareFunction = (array1, array2) => {
const result = [];
array2.forEach(arr2item => {
let selected = false;
for(let arr1item of array1){
selected = arr1item.id === arr2item.id;
if(selected)break;
}
result.push({id : arr2item.id , selected : selected});
});
return result;
}
console.log(compareFunction(array1 , array2));
This question already has answers here:
Simplest code for array intersection in javascript
(40 answers)
Closed 4 years ago.
Let's say we have:
var array1 = [{ id: 1 }, { id: 4}, { id: 3 }]
var array2 = [{ id: 1 }, { id: 2}]
I know you can concat the two arrays like this (without having duplicates):
Array.from(new Set(array1.concat(array2)))
Now, how to create a new array with only the objects that share the same values?
var array2 = [{ id: 1 }]
You can use .filter() and .some() to extract matching elements:
let array1 = [{ id: 1 }, { id: 4}, { id: 3 }]
let array2 = [{ id: 1 }, { id: 2}]
let result = array1.filter(({id}) => array2.some(o => o.id === id));
console.log(result);
Useful Resources:
Array.prototype.filter()
Array.prototype.some()
You could take a set with the id of the objects and filter array2
var array1 = [{ id: 1 }, { id: 4}, { id: 3 }] ,
array2 = [{ id: 1 }, { id: 2}],
s = new Set(array1.map(({ id }) => id)),
common = array2.filter(({ id }) => s.has(id));
console.log(common);
The requested sameness with identical objects.
var array1 = [{ id: 1 }, { id: 4}, { id: 3 }] ,
array2 = [array1[0], { id: 2}],
s = new Set(array1),
common = array2.filter(o => s.has(o));
console.log(common);
Assuming, by your definition, that the objects, even if they have the same structure, are not really the same object, I define an 'equality function', and then, with filter and some:
var array1 = [{ id: 1 }, { id: 4}, { id: 3 }]
var array2 = [{ id: 1 }, { id: 2}];
var equal = function(o1, o2) { return o1.id === o2.id };
var result = array2.filter(function(item1) {
return array1.some(function(item2) { return equal(item1, item2) });
});
console.log(result);
Joining of Arrays.
I'm in need of running a "Join Array" objects, but, I need duplicated objects to be removed, see:
Example
var objArray1 = [
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 }
];
var objArray2 = [
{ Id: 2, Name: 'Pedro', Order: 6 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
Actual code:
var result = _.union(objArray1,objArray2);
=> [
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 },
{ Id: 2, Name: 'Pedro', Order: 6 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
I need this result:
[
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
Basic I need join arrays with filter the one property, I need is possible with For but I would like a better solution
use underscore unique function as follows
var result = _.uniq(_.union(objArray1, objArray2), false, function(item){ return item.Id; });
not 100% sure if the false should be true
or, as seems to be a trend on SO - the sexy ES2015 version
var result = _.uniq(_.union(objArray1, objArray2), false, item => item.Id);