Manipulating objects in array of objects - javascript

I have this javascript objects:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}
I need to replace objects in arr1 with items from arr2 with same id.
Here how I achive the desired result:
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
And here is the result:
var arr1 = [{id:'124',name:'ttt'},
{id:'589',name:'em'},
{id:'45',name:'yyy'},
{id:'567',name:'eme'}];
But the problem that this solution:
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
Don't work in IE browser.
How can I change the row above to get the desired result in IE and chrome browsers?
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
console.log(res);

If you want to use Array.prototype.find() you would need to use recommended polyfill for browsers that don't support it.
See MDN Array.protoype.find() polyfill

If you don't want to use a polyfill, simply reduce the ids first then map.
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
var byIds = arr2.reduce((acc, item) => {
acc[item.id] = item;
return acc;
}, {})
var replacedArr1 = arr1.map(item => byIds[item.id] || item);

You have tagged lodash to this, so why not use lodash functions _ .find and _ .map instead of the native functions with limited browser support.
_.map(arr1, obj => _.find(arr2, o => o.id === obj.id) || obj);
To state the obvious, you also need to transpile the ES6 style arrow functions and what not to support older browsers.

Please, did you think of underscorejs you will find cross browser functions like find, filter and map
int lastIndex = 0
var result = _.map(arr1 , function(num){
if (arr1.id == arr2[lastIndex].id){
return arr2[lastIndex++];
}
return arr1;
});

You could iterate over arr2 and build an object as hash table and then iterate over arr1 for changing the elements.
var arr1 = [{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '45', name: 'eee' }, { id: '567', name: 'rrr' }],
arr2 = [{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }],
hash = Object.create(null);
arr2.forEach(function (a) {
this[a.id] = a;
}, hash);
arr1.forEach(function (a, i, aa) {
if (this[a.id]) {
aa[i] = this[a.id];
}
}, hash);
console.log(arr1);
Or use a version with short circuit.
var arr1 = [{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '45', name: 'eee' }, { id: '567', name: 'rrr' }],
arr2 = [{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }],
hash = Object.create(null);
hash.count = arr2.length;
arr2.forEach(function (a) {
this[a.id] = a;
}, hash);
arr1.some(function (a, i, aa) {
if (this[a.id]) {
aa[i] = this[a.id];
return !--this.count;
}
}, hash);
console.log(arr1);

What I would do is use a for loop to loop through each item in the 2nd array. Then loop through the first array to see if the ID matches. If it does, set the name to the name from the second array.
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}];
for (var i = 0; i < arr2.length; i++) {
for (var j = 0; j < arr1.length; j++) {
if (arr2[i].id === arr1[j].id) {
arr1[j].name = arr2[i].name;
}
}
}
here is a fiddle https://jsfiddle.net/k0grurx5/

Related

Get duplicates except first occurrence in array of objects

I want to be able to return duplicates except first occurance in an array of objects based of the place and keyword. Both should match and return the documents in an new array. Here is my trial run:
var things = [
{place: 'hello', keyword: 'hey', id: 0},
{place: 'hi', id: 1},
{place: 'hello', keyword: 'hey', id: 2},
{place: 'hello', keyword: 'man', id: 3}
]
var duplicates = [];
things.forEach((item, index) => {
if(things.indexOf(item.place) != index && things.indexOf(item.keyword) != index) {
duplicates.push(item);
}
});
Expected output:
[{place: 'hello', keyword: 'hey', id: 2}]
Any help would be great (without any frameworks, just ES6 or older). Thanks
EDIT: It should match multiple specified values such as keyword and place.
You could count the same keys and filter if the count is greater than one with an object for counting
const
getKey = o => keys.map(k => o[k]).join('|'),
keys = ['place', 'keyword'],
things = [{ place: 'hello', keyword: 'hey', id: 0 }, { place: 'hi', id: 1 }, { place: 'hello', keyword: 'hey', id: 2 }, { place: 'hello', keyword: 'man', id: 3 }],
hash = Object.create(null),
duplicates = things.filter(o =>
(k => (hash[k] = (hash[k] || 0) + 1) > 1)
(getKey(o))
);
console.log(duplicates);
The obvious solution is that you'll have to track the objects you have seen in order to do it how you want.
const seen = [];
const duplicates = [];
things.forEach(item => {
const sawItem = seen.find(seenItem => item.place === seenItem.place && item.keyword === seenItem.keyword)
if (sawItem) {
duplicates.push(sawItem);
} else {
seen.push(sawItem);
}
});
This isn't a very efficient algorithm however, so I'm curious to see a better way to do it.
You could group the items based on place and then get the first item from those groups with length > 1
const things = [{ place: 'hello', keyword: 'hey', id: 0 }, { place: 'hi', id: 1 }, { place: 'hello', keyword: 'hey', id: 2 }, { place: 'hello', keyword: 'man', id: 3 }];
const merged = things.reduce((r, a) => {
(r[a.place] = r[a.place] || []).push(a)
return r
}, {})
const final = Object.values(merged)
.filter(a => a.length > 1)
.map(a => a[1])
console.log(final)

Get list of duplicate objects in an array of objects

I am trying to get duplicate objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' },
{ id: 11, name: 'someName3' },
{ id: 12, name: 'someName4' }
];
Duplicate objects should return like below:
duplicate = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' }
];
You can use Array#reduce to make a counter lookup table based on the id key, then use Array#filter to remove any items that appeared only once in the lookup table. Time complexity is O(n).
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const lookup = values.reduce((a, e) => {
a[e.id] = ++a[e.id] || 0;
return a;
}, {});
console.log(values.filter(e => lookup[e.id]));
Let's say you have:
arr = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
So, to get unique items:
unique = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) === i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]);
Then, result will be
unique = [
{ id:10, name: 'someName1' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
And, to get duplicate ids:
duplicateIds = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) !== i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]["id"])
List of IDs will be
duplicateIds = [10]
Thus, to get duplicates objects:
duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));
Now you have it:
duplicate = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' }
]
Thanks https://reactgo.com/removeduplicateobjects/
You haven't clarified whether two objects with different ids, but the same "name" count as a duplicate. I will assume those do not count as a duplicate; in other words, only objects with the same id will count as duplicate.
let ids = {};
let dups = [];
values.forEach((val)=> {
if (ids[val.id]) {
// we have already found this same id
dups.push(val)
} else {
ids[val.id] = true;
}
})
return dups;
With lodash you can solve this with filter and countBy for complexity of O(n):
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could do the same with ES6 like so:
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))
You can use an array to store unique elements and use filter on values to only return duplicates.
const unique = []
const duplicates = values.filter(o => {
if(unique.find(i => i.id === o.id && i.name === o.name)) {
return true
}
unique.push(o)
return false;
})
With lodash you can use _.groupBy() to group elements by their id. Than _.filter() out groups that have less than two members, and _.flatten() the results:
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const result = _.flow([
arr => _.groupBy(arr, 'id'), // group elements by id
g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
_.flatten // flatten the results to a single array
])(values);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
An alternative based in #ggorlen solution with new Map() as accumulator (for better performance) and without unary operator ++ (not advised by default in projects with ESLint).
const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];
const lookup = values.reduce((a, e) => {
a.set(e.id, (a.get(e.id) ?? 0) + 1);
return a;
}, new Map());
console.log(values.filter(e => lookup.get(e.id) > 1));
Try this
function checkDuplicateInObject(propertyName, inputArray) {
var seenDuplicate = false,
testObject = {};
inputArray.map(function(item) {
var itemPropertyName = item[propertyName];
if (itemPropertyName in testObject) {
testObject[itemPropertyName].duplicate = true;
item.duplicate = true;
seenDuplicate = true;
}
else {
testObject[itemPropertyName] = item;
delete item.duplicate;
}
});
return seenDuplicate;
}
referred from : http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/

Get data from javascript array

I have this array of objects:
var frequencies = [{id:124,name:'qqq'},
{id:589,name:'www'},
{id:45,name:'eee'},
{id:567,name:'rrr'}];
And this array of id:
var idArray = [124,45];
I need create function that return array of string that contains value of the name that has idArray.
for example, the result according to the arrays above(frequencies and idArray) :
var result = var frequencies = ['qqq','eee'];
How can I implement this function?
Use Array#filter and Array#map methods.
var res = frequencies
//filter out object array
.filter(function(v) {
return idArray.indexOf(v.id) > -1;
})
// generate result arrray from filtered array
.map(function(v) {
return v.name
});
var frequencies = [{
id: 124,
name: 'qqq'
}, {
id: 589,
name: 'www'
}, {
id: 45,
name: 'eee'
}, {
id: 567,
name: 'rrr'
}];
var idArray = [124, 45];
var res = frequencies
//filter out object array
.filter(function(v) {
return idArray.indexOf(v.id) > -1;
})
// generate result arrray from filtered array
.map(function(v) {
return v.name
});
// with ES6 arrow function
//var res = frequencies.filter(v => idArray.indexOf(v.id) > -1).map(v => v.name);
console.log(res);
The same code with ES6 arrow function.
var res = frequencies
.filter(v => idArray.indexOf(v.id) > -1)
.map(v => v.name);
var frequencies = [{
id: 124,
name: 'qqq'
}, {
id: 589,
name: 'www'
}, {
id: 45,
name: 'eee'
}, {
id: 567,
name: 'rrr'
}];
var idArray = [124, 45];
var res = frequencies
.filter(v => idArray.indexOf(v.id) > -1)
.map(v => v.name);
console.log(res);
Or use a simple for loop
var res = [];
for (var i = 0; i < frequencies.length; i++) {
// check the id value present in array
// push the name property value if id present in array
if (idArray.indexOf(frequencies[i].id) > -1) {
res.push(frequencies[i].name);
}
}
var frequencies = [{
id: 124,
name: 'qqq'
}, {
id: 589,
name: 'www'
}, {
id: 45,
name: 'eee'
}, {
id: 567,
name: 'rrr'
}];
var idArray = [124, 45],
res = [];
for (var i = 0; i < frequencies.length; i++) {
// check the id value present in array
// push the name property value if id present in array
if (idArray.indexOf(frequencies[i].id) > -1) {
res.push(frequencies[i].name);
}
}
console.log(res);
ESNEXT code
frequencies
.filter(({id}) => idArray.includes(id))
.map(({name}) => name)

How to remove object from array by property value? [duplicate]

This question already has answers here:
Compare the elements of two arrays by Id and remove the elements from the one array that are not presented in the other
(3 answers)
Closed 6 years ago.
I have this javascript objects:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}
From arr1 I need remove all objects with the same id in arr2.
Here is the desired result of arr1:
var arr1 = [{id:'589',name:'www'},
{id:'567',name:'rrr'}]
What is elegant way to implement it in Javascript?
Here is a way to pick up the ID values on the fly. It binds the intermediate object to this via the optional thisArg argument of filter:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}];
arr1 = arr1.filter(function (el) {
return !this[el.id];
}, arr2.reduce(function (obj, el) {
return obj[el.id] = 1, obj;
}, {}));
console.log(arr1);
The intermediate object that is passed as the last argument to filter looks like this:
{ 45: 1, 124: 1 }
This makes it straightforward to check if an element of arr1 needs to be rejected or not.
I would get a condensed list of all IDs in arr2, then use indexOf or find from within a filter to exclude those from the revised arr1:
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var ids = arr2.map(function(it) {
return it.id;
});
var unique = arr1.filter(function(it) {
return ids.indexOf(it.id) === -1; // does not contain
});
console.log(unique);
If you don't want to map out the IDs -- although I would recommend doing so, since it means you don't have to walk through arr2 repeatedly -- you can use something like:
var unique = arr1.filter(function(it) {
return !arr2.some(function (other) {
return it.id === other.id;
});
});
I suggest to use a hash table for this.
var arr1 = [{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '45', name: 'eee' }, { id: '567', name: 'rrr' }],
arr2 = [{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }];
arr1 = arr1.filter(function (a) {
return !this[a.id];
}, function (hash) {
arr2.forEach(function (a) { hash[a.id] = true; });
return hash;
}(Object.create(null)));
console.log(arr1);
ES6
var arr1 = [{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '45', name: 'eee' }, { id: '567', name: 'rrr' }],
arr2 = [{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }];
arr1 = arr1.filter((hash =>
(arr2.forEach(a => hash[a.id] = true), a => !hash[a.id]))(Object.create(null)));
console.log(arr1);
I would highly encourage using underscorejs for this kind of object manipulation. From the documentation:
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
if you want to do the same thing on an object:
_.filter({"a":1,"b":2,"c":3,"d":4}, function(value,key){ return value % 2 == 0; })
In your specific case this would be the solution:
var newArray = _.filter(arr1,(object1) => {
return !_.contains(_.pluck(arr2,'id'),object1.id);
});
This is a chart showing improvements of Underscore and Lodash on native methods:
specifically on filter it shows more than a 20% improvement.
Taken from this article.

using javascript filter() with a flag

I want to return arr2 but want to prompt the user whether there's changes or not by comparing it with arr. with below's approach, I got id of undefined if arr2 have any missing item.
var arr = [{
id: 1,
name: 'something'
}, {
id: 2,
name: 'something2'
}]
var arr2 = [{
id: 1,
name: 'something'
}]
var result = arr.filter(function(obj, i) {
return obj.id == arr2[i].id;
});
document.write(JSON.stringify(result))
The problem in your code is that arr[1] is undefined and you are trying to get id property of undefined. Now what you can do is, get id's in array then get index and check based on that in filter.
var arr = [{
id: 1,
name: 'something'
}, {
id: 2,
name: 'something2'
}]
var arr2 = [{
id: 1,
name: 'something'
}];
var arrIds = arr2.map(function(v) {
return v.id;
});
var result = arr.filter(function(obj) {
var i = arrIds.indexOf(obj.id);
return i > -1 &&
obj.name == arr2[i].name; // check name property here
});
document.write(JSON.stringify(result))
Loop through arr2 in a callback of the .filter to test each item of arr2.
var arr = [{
id: 1,
name: 'something'
}, {
id: 2,
name: 'something2'
}]
var arr2 = [{
id: 1,
name: 'something'
}, {
id: 5,
name: 'something'
}, {
id: 8,
name: 'something'
}];
var isValInArr = function(arr, key, val) {
for (var i = 0, len = arr.length; i < len; i++) {
if (arr[i][key] === val) {
return true;
}
}
return false;
};
var result = arr.filter(function(obj, i) {
return isValInArr(arr2, 'id', obj.id);
});
document.write(JSON.stringify(result))

Categories

Resources