This question already has answers here:
A for-loop that compares two arrays looking for matching values
(4 answers)
Closed 8 years ago.
I have two arrays like
var array1 = [
{
id: '1',
name: 'test'
},
{
id: '2',
name: 'test2'
},
{
id: '3',
name: 'test3'
}
]
var array2=[
{
id: '2',
name: 'test2'
}
]
I want to loop through array 1 and find the same object and add more property in array 1
I have something like
for(var i=0; i < array1.length; i++) {
if(array2[i].id == array1[i].id){
alert('find!')
}
}
I understand my above codes don't work because the index is different. Can someone help me about this issue? Thanks a lot!
It’s time for ECMA5
var array1 = [
{
id: '1',
name: 'test',
foo: {
bar: 'bar',
quux: 'quux'
}
},
{
id: '2',
name: 'test2'
},
{
id: '3',
name: 'test3'
}
];
function equal(objA, objB) {
if(Object.keys(objA).length !== Object.keys(objB).length) {
return false;
}
var areEqual = Object.keys(objA).every(function(key) {
if(typeof objA[key] === "object") {
return equal(objA[key], objB[key]);
}
return objA[key] === objB[key];
});
return areEqual;
}
function hasElement(array, element) {
return array.some(function(el) {
return equal(el, element);
});
}
console.log(hasElement(array1, {
id: '1',
name: 'test',
foo: {
bar: 'bar',
quux: 'quux'
}
}));
Assuming the IDs in array2 are all unique, I would create an object whose keys are the IDs:
var obj2 = {};
for (var i = 0; i < array2.length; i++) {
obj2[array2[i].id] = array2[i];
}
Then I would use this to find matching elements:
for (var i = 0; i < array1.length; i++) {
if (obj2[array1[i].id]) {
alert("find!");
}
}
Related
Let's say I have two data sources that are fairly large, 3000+ entries each. They might look something like this...
const arOfObj1 = [
{ type: 'Something', properties: { name: 'ABC' } },
{ type: 'Something', properties: { name: 'DEF' } },
{ type: 'Something', properties: { name: 'GHI' } },
...and so on...
];
const arOfObj2 = [
{ name: 'ABC', stats: { age: 1, other: "Something" } },
{ name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ name: 'GHI', stats: { age: 3 } },
...and so on...
];
What would be the most efficient way of finding the name property that matches in each object, and appending the stats{...} (or other properties if they exist) from arOfObj2 to arOfObj1? so I would end up with something like this
const newArOfObj1 = [
{ type: 'Something', properties: { name: 'ABC', stats: { age: 1, other: "Something" } },
{ type: 'Something', properties: { name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ type: 'Something', properties: { name: 'GHI', stats: { age: 3 } },
...and so on...
]
My initial thought was to do something like this...
arOfObj1.forEach(obj1 => {
arOfObj2.forEach(obj2 => {
if (obj1.properties.name === obj2.name) {
obj1.stats = obj2.stats
}
})
});
Just not sure if there's a better way than to loop through arOfObj2 for each entry in arOfObj1
I don't know what the most efficient way is, and I don't really know how the memory works in javascript, but I have a way that works.
const arOfObj1 = [
{ type: 'Something', properties: { name: 'ABC' }},
{ type: 'Something', properties: { name: 'DEF' }},
{ type: 'Something', properties: { name: 'GHI' }}
];
const arOfObj2 = [
{ name: 'ABC', stats: { age: 1, other: "Something" } },
{ name: 'DEF', stats: { age: 2, isEnrolled: true } },
{ name: 'GHI', stats: { age: 3 } }
];
var names = new Map();
for(var i = 0, len = arOfObj2.length; i < len; i++){
var obj = arOfObj2[i];
names.set(obj.name, obj.stats);
}
for(var i = 0, len = arOfObj1.length; i < len; i++){
var properties = arOfObj1[i].properties;
properties.stats = names.get(properties.name);
}
console.log(arOfObj1);
What it does is loop through each object, saving the name and stats as the key and value in a Map. Then it loops through the first array of objects, adding the stats property that it gets from Map.get.
Memory is usually not the issue in the JavaScript applications and 3000+ array of objects is not that big for a modern machine. I'll have to assume that you're looking for a speed increase.
In case I am wrong and you're looking for something that would be more memory efficient then ignore my response and look in to batching with something like js batch to avoid loading up memory all at once and instead spreading the memory load across batches.
Assuming we're looking for a fastest way to complete the task.
Code:
map/find
arOfObj1.map(o => ({
...o,
properties: {
...o.properties,
stats: arOfObj2.find(a => a.name === o.properties.name).stats
}
}));
Simple double for loop
let result = [];
for (let i = 0; i < arOfObj1.length; i++) {
let o = arOfObj1[i];
let stats;
for (let j = 0; j < arOfObj2.length; j++) {
if (stats) break;
let a = arOfObj2[j];
if (a.name === o.properties.name) {
stats = a.stats;
}
}
result.push({
...o, properties: {...o.properties, stats}
});
}
Map set - while I was at it #programmerRaj posted his answer with this solution so look at his response, but I'll include it in my speed test below.
So now we can test these for speed:
Filling both arrays with 5000 items and running all solutions while measuring time.
let arOfObj1 = [];
let arOfObj2 = [];
for (let i = 0; i < 5000; i++) {
arOfObj1.push({type: 'Something', properties: { name: `name${i}` }});
arOfObj2.push({name: `name${i}`, stats: { age: i, other: "Something" }});
}
console.time('map/find');
arOfObj1.map(o => ({
...o,
properties: {
...o.properties,
stats: arOfObj2.find(a => a.name === o.properties.name).stats
}
}));
console.timeEnd('map/find');
console.time('for loop');
let result = [];
for (let i = 0; i < arOfObj1.length; i++) {
let o = arOfObj1[i];
let stats;
for (let j = 0; j < arOfObj2.length; j++) {
if (stats) break;
let a = arOfObj2[j];
if (a.name === o.properties.name) {
stats = a.stats;
}
}
result.push({
...o, properties: {...o.properties, stats}
});
}
console.timeEnd('for loop');
console.time('programmerRaj\'s solution');
var names = new Map();
for(var i = 0, len = arOfObj2.length; i < len; i++){
var obj = arOfObj2[i];
names.set(obj.name, obj.stats);
}
for(var i = 0, len = arOfObj1.length; i < len; i++){
var properties = arOfObj1[i].properties;
properties.stats = names.get(properties.name);
}
console.timeEnd('programmerRaj\'s solution');
Running test with Node lts (12) I get the following results:
map/find: 189.902ms
for loop: 188.912ms
programmerRaj's solution: 3.236ms
This question already has answers here:
How to add an array of values to a Set
(11 answers)
Closed 3 years ago.
Set only adds 1 copy of an array and I'm not sure why it doesn't keep adding other copies.
The function below takes in an array of trips with the travelers id and another array containing the travelers' ids and names. In this chunk of code,
if(item.type === type){
store.add(...item.travelers);
}
I expected that 123,456 and 789 will be added to the set. However, only 123 and 456 are added. Why doesn't the Set add in the second copy (which would be 456 and 789)?
const travelers = [
{
id: 123,
name: 'John'
},
{
id: 456,
name: 'Raymond'
},
{
id: 789,
name: 'Mary'
},
];
const trip = [
{
type: 'car',
travelers: [123, 456]
},
{
type: 'flight',
travelers: []
},
{
type: 'car',
travelers: [456, 789]
},
];
function determinePeopleForEachTripType(arr, travelers, type){
const result = [];
let store = new Set();
for(let i = 0; i< arr.length; i++){
let item = arr[i];
if(item.type === type){
store.add(...item.travelers);
}
}
store.forEach(eachStore =>{
for(let j = 0; j< travelers.length; j++){
if(eachStore === travelers[j].id){
result.push(travelers[j].name)
}
}
})
return result;
}
determinePeopleForEachTripType(trip, travelers, 'car');
Expected result: Set contains [123, 456, 789]. But actual output is Set contains [123, 456]. What are some possible ways to fix this code?
Set.prototype.add only takes one argument, and that's the one element - you need to iterate through travellers and add each item separately:
item.travelers.forEach(Set.prototype.add, store);
const travelers = [
{
id: 123,
name: 'John'
},
{
id: 456,
name: 'Raymond'
},
{
id: 789,
name: 'Mary'
},
];
const trip = [
{
type: 'car',
travelers: [123, 456]
},
{
type: 'flight',
travelers: []
},
{
type: 'car',
travelers: [456, 789]
},
];
function determinePeopleForEachTripType(arr, travelers, type){
const result = [];
let store = new Set();
for(let i = 0; i< arr.length; i++){
let item = arr[i];
if(item.type === type){
for (let traveler of item.travelers) {
store.add(traveler);
}
}
}
store.forEach(eachStore =>{
for(let j = 0; j< travelers.length; j++){
if(eachStore === travelers[j].id){
result.push(travelers[j].name)
}
}
})
return result;
}
const result = determinePeopleForEachTripType(trip, travelers, 'car');
console.log(result)
I am trying to check if object array A includes objects from B.
let A = [
{ name: "Max" },
{ name: "Jhon" },
{ name: "Naton" },
]
let B = [
{ name: "Max" },
{ name: "Naton" },
]
So B has two objects that is in array A. How to check this ?
I am trying to achieve it with includes :
for(let entry of this.b){
if(this.a.includes(entry)){
console.log('includes');
}
}
But I get false on includes.
The method Array.includes() compare the entries of the array with the given value. Because your array entries are objects, it will not match. You have to loop at the array yourself and make the comparison.
Array.some() loops on an array and returns true if you returns true at least one. This method is useful when you want to verify something. In our example, we want to verify if the array a contains the b entry.
const a = [{
name: 'Max',
},
{
name: 'Jhon',
},
{
name: 'Naton',
},
];
const b = [{
name: 'Max',
},
{
name: 'Naton',
},
{
name: 'Daddy',
},
];
console.log(b.map(x => a.some(y => y.name === x.name)));
If I break it down :
const a = [{
name: 'Max',
},
{
name: 'Jhon',
},
{
name: 'Naton',
},
];
const b = [{
name: 'Max',
},
{
name: 'Naton',
},
{
name: 'Daddy',
},
];
// Loop on every entry of the b array
b.forEach((x) => {
// x here represent one entry
// first it will worth { name: 'Max' }, then { name: 'Naton' } ...
// for each value we are going to look at a if we can find a match
const isThereAMatch = a.some((y) => {
// y here is worth one entry of the a array
if (y.name === x.name) return true;
return false;
});
if (isThereAMatch === true) {
console.log(`We have found ${x.name} in a`);
} else {
console.log(`We have not found ${x.name} in a`);
}
});
You have to use another loop, then check the property name:
var a = [
{name: "Max"},
{name: "Jhon"},
{name: "Naton"},
];
var b = [
{name: "Max"},
{name: "Naton"},
];
for(let entry of b){
for(let entry2 of a){
if(entry2.name == entry.name){
console.log('includes', entry.name);
}
}
}
OR: You can use string version of object to check with includes():
var a = [
{name: "Max"},
{name: "Jhon"},
{name: "Naton"},
];
var b = [
{name: "Max"},
{name: "Naton"},
];
var aTemp = a.map(i => JSON.stringify(i));
var bTemp = b.map(i => JSON.stringify(i));
for(let entry of bTemp){
if(aTemp.includes(entry)){
console.log('includes', entry);
}
}
When you use Array#includes() method it will always return false because it's comparing objects which aren't equal because they aren't referencing the same object.
You should compare objects properties and not whole objects, you can do it using Array#some() method like this:
for (let entry of this.b) {
if (this.b.some(x => x.name === entry.name)) {
console.log('includes');
}
}
Demo:
A = [{
name: "Max"
},
{
name: "Jhon"
},
{
name: "Naton"
},
]
B = [{
name: "Max"
},
{
name: "Naton"
},
]
//Filter objects that exists in both arrays
let result = A.filter(el=> B.some(x => x.name === el.name));
console.log(result);
I have an array with objects of unknown depth, like this
var objects = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar',
childs: [{
id: 3,
name: 'baz',
childs: [{
id: 4,
name: 'foobar'
}]
}]
}];
I would like to be able to filter a specific child object by it's id.
Currently I am using this little lodash script (referred from this question) but it works only with objects not deeper than one level. So searching for id: 1 and id: 2 would work fine while searching for id: 3 or id: 4 would return undefined.
function deepFilter(obj, search) {
return _(obj)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children'));
})
.flatten()
.find(search);
}
A little JSfiddle.
You could take an iterative and recursive approach.
function find(id, array) {
var result;
array.some(o => o.id === id && (result = o) || (result = find(id, o.children || [])));
return result;
}
var objects = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar', children: [{ id: 3, name: 'baz', children: [{ id: 4, name: 'foobar' }] }] }];
console.log(find(1, objects));
console.log(find(2, objects));
console.log(find(3, objects));
console.log(find(4, objects));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do that recursively like so:
function deepFind(arr, search) {
for(var obj of arr) {
if(search(obj)) {
return obj;
}
if(obj.childs) {
var deepResult = deepFind(obj.childs, search);
if(deepResult) {
return deepResult;
}
}
}
return null;
}
Then use it like so:
var result = deepFind(objects, function(obj) {
return obj.id === myId;
});
Example:
function deepFind(arr, search) {
for(var obj of arr) {
if(search(obj)) {
return obj;
}
if(obj.childs) {
var deepResult = deepFind(obj.childs, search);
if(deepResult) {
return deepResult;
}
}
}
return null;
}
var objects = [{id: 1,name: 'foo'}, {id: 2,name: 'bar',childs: [{id: 3,name: 'baz',childs: [{id: 4,name: 'foobar'}]}]}];
console.log("ID 1:", deepFind(objects, obj => obj.id === 1));
console.log("ID 4:", deepFind(objects, obj => obj.id === 4));
console.log("ID 7:", deepFind(objects, obj => obj.id === 7));
You need to call the function recursively in order to target child object. Try following
Iterate over the array and for each object check whether the id is found. If yes, break and return the result, else continue to search in the child (if exists).
Approach 1 : Traverses the tree branch by branch
Using this approach, first the code traverses for first element till the last child, then second element this last child and so on.
var objects = [{id: 1,name: 'foo'}, {id: 2,name: 'bar',childs: [{id: 3,name: 'baz',childs: [{id: 4,name: 'foobar'}]}]}];
function findObject(arr, id) {
var result;
for (let i = 0 ; i < arr.length; i++) {
if(arr[i].id === id) {
result = arr[i];
break;
}
if(arr[i].childs) {
result = findObject(arr[i].childs, id);
if(result) break;
}
}
return result;
}
console.log(findObject(objects, 4));
Approach 2 : Traverses the tree depth by depth
Using this approach, first the code traverses for first level elements, then second level elements and so on.
var objects = [{id: 1,name: 'foo'}, {id: 2,name: 'bar',childs: [{id: 3,name: 'baz',childs: [{id: 4,name: 'foobar'}]}]}];
function findObject(arr, id) {
var result;
var children = [];
for (let i = 0 ; i < arr.length; i++) {
if(arr[i].id === id) {
result = arr[i];
break;
}
if(arr[i].childs) {
children = [...children, ...arr[i].childs];
}
}
if(!result && children.length) {
result = findObject(children, id);
}
return result;
}
console.log(findObject(objects, 4));
You can have a recursive function and check for the child
var objects = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar',
childs: [{
id: 3,
name: 'baz',
childs: [{
id: 4,
name: 'foobar'
}]
}]
}];
let tempArray = [];
function doRecursiveSearch(obj, id) {
obj.forEach(function(item) {
console.log(item)
if (item.id === id) {
tempArray.push(item)
} else {
if (item.childs && Array.isArray(item.childs)) {
console.log(item)
doRecursiveSearch(item.childs, id)
}
}
})
}
doRecursiveSearch(objects, 4)
console.log(tempArray)
Suppose I have the following arrays:
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, field: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
]
var third = [
{ id: 2, data: 'some2' },
{ id: 5, data: 'some5' },
{ id: 6, data: 'some6' }
]
I want to merge them to get the following result:
var result = [
{ id: 1, name: 'first', field: undefined, data: undefined },
{ id: 2, name: 'second', field: 'foo2', data: 'some2' },
{ id: 3, name: 'third', field: 'foo3', data: undefined },
{ id: 4, name: undefined, field: 'foo4', data: undefined },
{ id: 5, name: undefined, field: undefined, data: 'some5' },
{ id: 6, name: undefined, field: undefined, data: 'some6' }
]
How could I do it with JavaScript?
You should get all existed keys and after create new Objects with fill "empty" keys:
function mergeArrays(){
var keys = {};
//save all existed keys
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
for(var key in arguments[i][j]){
keys[key] = true;
}
}
}
var res = [];
for(var i=arguments.length;--i;){
for(var j=arguments[i].length;--j;){
//set clone of object
var clone = JSON.parse(JSON.stringify(arguments[i][j]));
for(var key in keys){
if(!(key in clone)){
clone[key] = undefined;
}
}
res.push(clone);
}
}
return res;
}
https://jsfiddle.net/x3b0tk3g/
There is no simple solution for what you want. Here is my suggestion.
var first = [
{ id: 1, name: 'first' },
{ id: 2, name: 'second' },
{ id: 3, name: 'third' }
]
var second = [
{ id: 2, filed: 'foo2' },
{ id: 3, field: 'foo3' },
{ id: 4, field: 'foo4' }
];
var third = [
{ id: 2, data: 'some2' },
{ id: 4, data: 'some4' },
{ id: 6, data: 'some6' }
];
var result = {};
first.concat(second,third).forEach(function(item){
var id = item.id;
var row = result[id];
if(!row){
result[id] = item;
return;
}
for(var column in item){
row[column] = item[column];
}
});
var finalResult = Object.keys(result).map(function(id){
return result[id];
});
console.log(finalResult);
fiddle: http://jsfiddle.net/bs20jvnj/2/
function getByProperty(arr, propName, propValue) {
for (var i = 0; i < arr.length; i++) {
if (arr[i][propName] == propValue) return arr[i];
}
}
var limit = first.length + second.length + third.length;
var res = [];
for (var i = 1; i < limit; i++) {
var x = $.extend({}, getByProperty(first, "id", i), getByProperty(second, "id", i), getByProperty(third, "id", i));
console.log(x["id"]);
if (x["id"] === undefined) x["id"] = i;
res.push(x);
}
console.log(res);
There's probably a shorter way to solve this, but this covers all the steps, including ensuring that there are default properties that are undefined if not found. It also takes any number of input arrays, and you can specify what default keys you require if they're not already covered by the keys in the existing objects, so pretty future-proof for your needs.
// merges the key/values of two objects
function merge(a, b) {
var key;
if (a && b) {
for (key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
}
return a;
}
function concatenate() {
var result = [];
var args = arguments[0];
for (var i = 0, l = args.length; i < l; i++) {
result = result.concat(args[i]);
}
return result;
}
// return a default object
function getDefault() {
return {
id: undefined,
name: undefined,
data: undefined,
field: undefined
};
}
// loop over the array and check the id. Add the id as a key to
// a temporary pre-filled default object if the key
// doesn't exist, otherwise merge the existing object and the
// new object
function createMergedArray(result) {
var temp = {};
var out = [];
for (var i = 0, l = result.length; i < l; i++) {
var id = result[i].id;
if (!temp[id]) temp[id] = getDefault();
merge(temp[id], result[i]);
}
// loop over the temporary object pushing the values
// into an output array, and return the array
for (var p in temp) {
out.push(temp[p]);
}
return out;
}
function mergeAll() {
// first concatenate the objects into a single array
// and then return the results of merging that array
return createMergedArray(concatenate(arguments));
}
mergeAll(first, second, third);
DEMO