How can I compare two arrays of objects and update a key if an object exists in both arrays?
$scope.listOne = [
{id: 1, selected: false},
{id: 2, selected: false},
{id: 3, selected: false}
];
$scope.listTwo = [
{id: 4, color: orange},
{id: 5, color: blue},
{id: 2, color: green}
];
Using the above objects, how can I compare them and have listOne[1].selected updated to true?
Here, i am trying to loop through listone and checking if there is such key in listtwo if so making listone's selected property to true
This is done in vanila javascript
var listOne = [{
id: 1,
selected: false
}, {
id: 2,
selected: false
}, {
id: 3,
selected: false
}];
var listTwo = [{
id: 4,
color: "orange"
}, {
id: 5,
color: "blue"
}, {
id: 2,
color: "green"
}];
angular.forEach(listOne, function(value) {
for (var key in listTwo) {
if (listTwo[key]["id"] == value.id) {
value.selected = true;
}
}
});
console.log(listOne);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
Hope this helps
My first thought that jumps to mind is to use a lodash function:
let a = [{ id: 1, selected: false }];
let b = [{ id: 1, selected: false }, { id: 2, selected: true }];
let result = _.intersectionWith(a, b, _.isEqual);
'result' should be an array with one element, that part which match (ie., 'a').
Look into lodash _.some (https://lodash.com/docs/4.16.6#some)
Otherwise you could loop through the arrays, JSON.stringify each object and check for equality that way like this answer:
Object comparison in JavaScript
In just plain Javascript:
$scope.listOne.filter(function(item) {
return !!$scope.listTwo.find(function(innerItem) {
innerItem.id == item.id;
});
})
.forEach(function(item) {
item.selected = true;
});
Related
I want to concatenate 2 lists in immutable.js.
Both lists have this structure: { id, value }
The algorithm concatenate should do this:
If an ID exists in both list1 and list2 take the value from list2.
let list1 = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
]
let list2 = [
{ id: 1, value: 'quux' }, // id 1 exists in list1
{ id: 4, value: 'asd' },
]
let result = [
{ id: 1, value: 'quux' }, // from list 2
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
{ id: 4, value: 'asd' },
]
If Immutable.js has this functionality with another type (eg. Dictionary), I could also use that.
Algorithms for union
First you have to maintain two map with key as id and value as object then check for length of array which is of bigger size and pass the bigger size array with small size map to merged function there you can iterate over the array and check if it's exists in the map if yes then update the object and delete that row from map otherwise add the object into output. After the for loop complete check if map has element present then push all the values from map into output array and return;
index.js
const old = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
];
const newa = [
{ id: 1, value: 'quux' }, // update
{ id: 4, value: 'asd' }, // push
];
function merged(input,filterMap){
var output = [];
input.forEach(function(eachRow){
if(filterMap.hasOwnProperty(eachRow.id)){
output.push(Object.assign(eachRow,filterMap[eachRow.id]));
delete filterMap[eachRow.id];
}else{
output.push(eachRow);
}
});
if(Object.keys(filterMap).length > 0){
output = output.concat(Object.values(filterMap));
}
return output;
}
function parseData(first,second){
var mapFirst = {},
mapSecond = {};
var output = [];
first.forEach(function(eachRow){
mapFirst[eachRow.id] = eachRow;
});
second.forEach(function(eachRow){
mapSecond[eachRow.id] = eachRow;
});
if(first.length > second.length){
return merged(first,mapSecond);
}else{
return merged(second,mapFirst);
}
}
console.log(parseData(old,newa));
Working jsFiddle demo - https://jsfiddle.net/qz25hnmf/
So, if I have two arrays...
const arr1 = [ { id: 1: newBid: true } ];
const arr2 = [ { id: 1, newBid: false }, { id: 2, newBid: false } ];
I want to wind up with an array that is like this
[ { id: 1, newBid: false }, { id: 2, newBid: false } ]
BUT... I want the { id: 1, newBid: true } to be from arr1 and not arr2
I was using Lodash uniqBy(arr1, arr2, ['id']), but it deletes the 1st occurance, not the 2nd
You should use lodash mergeWith function.
const arr1 = [{
id: 1,
newBid: true
}];
const arr2 = [{
id: 1,
newBid: false
}, {
id: 2,
newBid: false
}];
function customizer(firstValue, secondValue) {
if(firstValue)
return firstValue;
else
return secondValue;
}
console.log(_.mergeWith(arr1, arr2, customizer));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
I find using an object as a map to be the easiest way to deal with this kind of problems.
const arr1 = [ { id: 1, newBid: true } ];
const arr2 = [ { id: 1, newBid: false }, { id: 2, newBid: false } ];
const map = {};
function execute(array) {
for(let i = 0; i < array.length; i++) {
const item = array[i];
map[item.id] = item;
}
}
execute(arr1);
execute(arr2);
console.log(Object.values(map))
I have an array of objects with parentId and sort values that I'd like to put into an array with nested 'children' and sorted appropriately.
For example, here's the data:
[{
id: 1,
sort: 2,
parentId: null,
name: 'A'
}, {
id: 2,
sort: 1,
parentId: 1,
name: 'A.1'
}, {
id: 3
sort: 2,
parentId: 1,
name: 'A.2'
}, {
id: 4,
sort: 1,
parentId: null,
name: 'B'
}]
The way I'd like to transform this would be such as:
[{
id: 4,
sort: 1,
parentId: null,
name: 'B',
children: []
}, {
id: 1,
sort: 2,
parentId: null,
name: 'A',
children: [{
id: 2,
sort: 1,
parentId: 1,
name: 'A.1'
}, {
id: 3
sort: 2,
parentId: 1,
name: 'A.2'
}]
}]
This is sorted (id 4 being at the top, since sort is 1) and the children are nested and also sorted accordingly.
Any suggestions on a good way to do this? I can recursively loop through to apply children, but not sure how I can maintain sorting on this.
This is a proposal which sorts first and filters after that.
The sorting takes the properties parentId and sort. This is necessary for the next step, because the "filtering" needs a sorted array.
Later the array is filterd with Array#filter(), Here is thisArgs used for referencing nodes for a possible insertation of children.
Edit: Update for unsorted (id/parentId) data.
var array = [{ id: 1, sort: 2, parentId: null, name: 'A' }, { id: 2, sort: 1, parentId: 1, name: 'A.1' }, { id: 3, sort: 2, parentId: 1, name: 'A.2' }, { id: 4, sort: 1, parentId: null, name: 'B' }],
nested;
array.sort(function (a, b) {
return (a.parentId || -1) - (b.parentId || -1) || a.sort - b.sort;
});
nested = array.filter(function (a) {
a.children = this[a.id] && this[a.id].children;
this[a.id] = a;
if (a.parentId === null) {
return true;
}
this[a.parentId] = this[a.parentId] || {};
this[a.parentId].children = this[a.parentId].children || [];
this[a.parentId].children.push(a);
}, Object.create(null));
document.write('<pre>' + JSON.stringify(nested, 0, 4) + '</pre>');
I gave it a try and came back, and there are already other answers, but I'm posting it anyway.
This method modifies the original Array:
var items = [{id: 1,sort: 2,parentId: null,name: 'A'}, {id: 2,sort: 1,parentId: 1,name: 'A.1'}, {id: 3,sort: 2,parentId: 1,name: 'A.2'}, {id: 4,sort: 1,parentId: null,name: 'B'}];
function generate_tree(arr){
var references = {};
arr.sort(function(a,b){
// Save references to each item by id while sorting
references[a.id] = a; references[b.id] = b;
// Add a children property
a.children = []; b.children = [];
if(a.sort > b.sort) return 1;
if(a.sort < b.sort) return -1;
return 0;
});
for(var i=0; i<arr.length; i++){
var item = arr[i];
if(item.parentId !== null && references.hasOwnProperty(item.parentId)){
references[item.parentId].children.push(arr.splice(i,1)[0]);
i--; // Because the current index now contains the next item
}
}
return arr;
}
document.body.innerHTML = "<pre>" + JSON.stringify(generate_tree(items), null, 4) + "</pre>";
I'd create a new data structure, to look like:
{ 1: {
id: 1,
sort: 2,
parentId: null,
name: 'A'
},
2: {
id: 4,
sort: 1,
parentId: null,
name: 'B'
}
}
Things to notice: the new structure is an object, not an array, that contains only the topmost elements in it (the ones with parentId null)
Then, do a for over the original array, and assign new_obj[ orig_arr_curr_elem[parentId] ].children.push(orig_arr_curr_elem)
Then create a new array with the elems from new_obj sort() the (or the children property) however you want
Code that implements the steps 1 and 2 (run this using node):
var util = require('util');
var old_arr = [{
id: 1,
sort: 2,
parentId: null,
name: 'A'
}, {
id: 2,
sort: 1,
parentId: 1,
name: 'A.1'
}, {
id: 3,
sort: 2,
parentId: 1,
name: 'A.2'
}, {
id: 4,
sort: 1,
parentId: null,
name: 'B'
}];
var new_obj = {};
for (var i = 0; i < old_arr.length; i++){
if ( old_arr[i].parentId == null )
new_obj[ old_arr[i].id ] = old_arr[i];
}
for (var i = 0; i < old_arr.length; i++){
if ( old_arr[i].parentId == null ) continue;
new_obj[ old_arr[i].parentId ].children = new_obj[ old_arr[i].parentId ].children || [];
new_obj[ old_arr[i].parentId ].children.push( old_arr[i] );
}
console.log(util.inspect(new_obj, {showHidden: false, depth: null}));
I have an array object:
[
{ id:1, name: 'Pedro'},
{ id:2, name: 'Miko'},
{ id:3, name: 'Bear'},
{ id:4, name: 'Teddy'},
{ id:5, name: 'Mouse'}
]
And I have an array with ids [1, 3, 5],
How can I filter the array object to leave records only with id's from the second one?
If Array.includes() is supported, you can use it with Array.filter() to get the items:
const array = [
{ id: 1, name: 'Pedro'},
{ id: 2, name: 'Miko'},
{ id: 3, name: 'Bear'},
{ id: 4, name: 'Teddy'},
{ id: 5, name: 'Mouse'}
];
const filterArray = [1,3,5];
const result = array.filter(({ id }) => filterArray.includes(id));
console.log(result);
If includes is not supported, you can use Array.indexOf() instead:
var array = [
{ id: 1, name: 'Pedro'},
{ id: 2, name: 'Miko'},
{ id: 3, name: 'Bear'},
{ id: 4, name: 'Teddy'},
{ id: 5, name: 'Mouse'}
];
var filterArray = [1,3,5];
var result = array.filter(function(item) {
return filterArray.indexOf(item.id) !== -1;
});
console.log(result);
Maybe take a Array.prototype.reduce in combination with an Array.prototype.some. This keeps the order of the given array need.
var data = [
{ id: 3, name: 'Bear' },
{ id: 4, name: 'Teddy' },
{ id: 5, name: 'Mouse' },
{ id: 1, name: 'Pedro' },
{ id: 2, name: 'Miko' },
],
need = [1, 3, 5],
filtered = need.reduce(function (r, a) {
data.some(function (el) {
return a === el.id && r.push(el);
});
return r;
}, []);
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
To keep the order of data you can use Array.prototype.filter:
var data = [
{ id: 3, name: 'Bear' },
{ id: 4, name: 'Teddy' },
{ id: 5, name: 'Mouse' },
{ id: 1, name: 'Pedro' },
{ id: 2, name: 'Miko' },
],
need = [1, 3, 5],
filtered = data.filter(function (a) {
return ~need.indexOf(a.id);
});
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
In case the data set is small, you are ok with any of the offered solution (ones that use indexOf).
However, these solutions are O(n^2) ones, therefore, given the data set big enough, the lag can become noticeable. In this case, you should build an index prior to selecting elements.
Example:
function filterFast(data, ids) {
var index = ids.reduce(function(a,b) {a[b] = 1; return a;}, {});
return data.filter(function(item) {
return index[item.id] === 1;
});
}
And some benchmarking can be tested here.
You can use the filter method on your Array:
var data = [
{ id:1, name: 'Pedro'},
{ id:2, name: 'Miko'},
{ id:3, name: 'Bear'},
{ id:4, name: 'Teddy'},
{ id:5, name: 'Mouse'}
];
var ids = [1, 3, 5];
var filteredData = filterData(data, 'id', ids[1]);
function filterData(data, prop, values) {
return data.filter(function(item) {
return ~values.indexOf(item[prop]); // ~ returns 0 if indexOf returns -1
});
}
See it in action in this JSFiddle.
Or if you are using jQuery, another option may be:
var arr1 = [1, 3, 5],
arr2 = [{ id: 1, name: 'Pedro' },
{ id: 2, name: 'Miko' },
{ id: 3, name: 'Bear' },
{ id: 4, name: 'Teddy' },
{ id: 5, name: 'Mouse' }],
filtered = $.grep(arr2, function (item) {
if (arr1.indexOf(item.id) > -1) {
return true;
}
});
You can use a for loop on the object array and check hasOwnProperty in another for loop for each ids in [1,3,5] (break out of the loop once an id found). (And break out of the bigger for-loop once all ids are found) If your array object is ordered (e.g. elements sorted from smallest id to biggest id) and so are your list, this solution should be quite efficient.
var c = 0;
for(var i =0; i< objects.length; i++){
for(var v =0; v< list.length; v++)
if(objects[i].hasOwnProperty(list[v])){
delete objects[i]; c++; break;
}
if(c===list.length) break;
}
or use array.splice( i, 1 ); if you don't want an empty slot.
Using filter and indexOf will do the trick:
var filteredArray = dataArray.filter(function(obj) {
return idsArray.indexOf(obj.id) > -1;
});
However, indexOf has linear performance, and it will be called lots of times.
In ES6 you can use a set instead, whose has call has sublinear performance (on average):
var idsSet = new Set(idsArray),
filteredArray = dataArray.filter(obj => idsSet.has(obj.id));
Assuming the toString method of your ids is injective, you can achieve something similar in ES5:
var idsHash = Object.create(null);
idsArray.forEach(function(id) {
idsHash[id] = true;
});
var filteredArray = dataArray.filter(function(obj) {
return idsHash[obj.id];
});
I want to get an object from an array of objects, then update it.
var myObjs = [{ id: 1, name: "foo"}, { id: 2, name: "bar" }];
var myObjectToUpdate = _.findWhere(myObjs, { id: 2 });
myObjectToUpdate = { id: 2, name: "boop" };
myObjs[1] // { id: 2, name: "boop" }
Currently when I update myObject in the 3rd line, it does not update the array of objects. I'm assuming it is updating the new variable instead of referencing.
What is the correct way to do this?
#E_net4 is correct, you are reassigning the object you just found.
If all you need to do is update the name, try this:
var myObjs = [{ id: 1, name: "foo"}, { id: 2, name: "bar" }];
var myObjectToUpdate = _.findWhere(myObjs, { id: 2 });
myObjectToUpdate.name = "boop";
myObjs[1] // { id: 2, name: "boop" }
I guess this is what you want. You have, in your code, misconceptions. Please read my code and compare both.
Hope it helps!
function fn(arr, toReplace, newValue) {
for(var x in arr) {
for(var k in toReplace) {
if(arr[x][k] == toReplace[k]) {
arr[x] = newValue;
}
}
}
return arr;
};
var arr = [{ id: 1, name: "foo"}, { id: 2, name: "bar" }];
var newValue = {id: 2, name: "boop"};
arr = fn(arr, {id: 2}, newValue);
console.log(arr);