Set order to array of object in javascript - javascript

I need to find a simplest way for setting order to array of objects.
For example, there is an array:
var array = [
{id: 1, name: "Matt"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"},
{id: 4, name: "Bruce"}
];
and I have provided
var order = [1,4,2,3];
which refers to object id property of array items.
Now I need to reorder array so it should be like:
var array = [
{id: 1, name: "Matt"},
{id: 4, name: "Bruce"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"}
]

Use Array#sort method for sorting and inside custom sort function use Array#indexOf method to get index.
var array = [{
id: 1,
name: "Matt"
}, {
id: 2,
name: "Jack"
}, {
id: 3,
name: "Morgan"
}, {
id: 4,
name: "Bruce"
}];
var order = [1, 4, 2, 3];
array.sort(function(a, b) {
// sort based on the index in order array
return order.indexOf(a.id) - order.indexOf(b.id);
})
console.log(array);

You can also use reduce() on [1,4,2,3] array to return object where keys will be elements and values will be index of each element and then sort by that object.
var array = [
{id: 1, name: "Matt"},
{id: 2, name: "Jack"},
{id: 3, name: "Morgan"},
{id: 4, name: "Bruce"}
];
var s = [1,4,2,3].reduce((r, e, i) => {return r[e] = i, r}, {});
var result = array.sort(function(a, b) {
return s[a.id] - s[b.id];
});
console.log(result)

I guess anything that involves sort can not be more efficient than an O(2n) solution. So i would like to do this job with two reduces as follows;
var arr = [{id: 1, name: "Matt"}, {id: 2, name: "Jack"}, {id: 3, name: "Morgan"}, {id: 4, name: "Bruce"}],
order = [1,4,2,3],
lut = order.reduce((t,e,i) => (t[e] = i,t),{}),
result = arr.reduce((res,obj) => (res[lut[obj.id]] = obj, res) ,[]);
console.log(result);

Related

Compare two array of objects and remove objects from first array if a property value is matched

I have 2 array of objects like
const arrayOne = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 3, name: 'three'}];
const arrayTwo = [{id: 2, name: 'two'}, {id: 3, name: 'three'}];
Here, I need to compare both these arrays and remove matching objects from arrayOne, which should finally give
this.arrayOne = [{id: 1, name: 'one'}];
I tried like below but it is removing all objects from the array
this.arrayOne = this.arrayOne.filter(o1 => this.arrayTwo.some(o2 => o1.id === o2.id));
What I am doing wrong here? Please suggest. Thanks
const arrayOne = [
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" },
];
const arrayTwo = [
{ id: 2, name: "two" },
{ id: 3, name: "three" },
];
const arrayTwoIds = new Set(arrayTwo.map((el) => el.id));
const arrayOneFiltered = arrayOne.filter((el) => !arrayTwoIds.has(el.id));
console.log(arrayOneFiltered);
// [ { id: 1, name: 'one' } ]
Depending on the size of the array, creating a set can improve performance, as you do not need to loop over arrayTwo arrayOne.length times but only once. After that, you can look up the existence of an id in arrayTwo in constant time.
Yet, as pointed out in another answer, this is not necessary if the arrays are small (like in your example). In this case, you could also use this one-liner:
arrayOne = arrayOne.filter((elOne) => !arrayTwo.some((elTwo) => elOne.id === elTwo.id));
Here, arrayOne would need to be mutable, i.e. defined with let.
You can find it by comparing it with id.
And arrayOne must be a let.
let arrayOne = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 3, name: 'three'}];
const arrayTwo = [{id: 2, name: 'two'}, {id: 3, name: 'three'}];
arrayOne = arrayOne.filter(one => !arrayTwo.find(two => one.id == two.id));
console.log(arrayOne);
const arrayOne = [{
id: 1,
name: 'one'
}, {
id: 2,
name: 'two'
}, {
id: 3,
name: 'three'
}];
const arrayTwo = [{
id: 2,
name: 'two'
}, {
id: 3,
name: 'three'
}];
const arrayTwoId = arrayTwo.map(el => (el.id)); // extract id from arrayTwo
const result = arrayOne.filter(el => !arrayTwoId.includes(el.id));
console.log(result);
Extract all the ids from the arrayTwo.
filter those objects who do not match the array of ids of arrayTwo.
Your way is correct. but you miss the not operation (!) before arrayTwo.some.
So the correct way is this:
const arrayOne = [
{id: 1, name: 'one'},
{id: 2, name: 'two'},
{id: 3, name: 'three'}
];
const arrayTwo = [
{id: 2, name: 'two'},
{id: 3, name: 'three'}
];
// Shared Items between arrayOne and arrayTwo (this what you done)
const sharedObjects = arrayOne.filter(o1 => arrayTwo.some(o2 => o1.id === o2.id));
console.log(sharedObjects);
// arrayOne - arrayTwo (this is what you want)
const arrayOneUniqueObjects = arrayOne.filter(o1 => !arrayTwo.some(o2 => o1.id === o2.id));
console.log(arrayOneUniqueObjects);
Also, You can find more details here:
bobbyhadz.com/blog/javascript-get-difference-between-two-arrays-of-objects

How to sort an array of objects using another array for reference?

i want to sort an array of objects having id each object using another array that only has the ids, for example:
object = [
{id: 2, name: carlos},
{id: 1, name: maria},
{id: 4, name: juan},
{id: 3, name: pepe}, //this is the array that i want to be sorted or create a copy to return it
]
[1,2,3,4,5] //this is the array that i will use as reference to sort the first one
the final result should be:
object = [
{id: 1, name: maria},
{id: 2, name: carlos},
{id: 3, name: pepe},
{id: 4, name: juam}, //this is the array that i want to be sorted or create a copy to return it
]
im using two maps, but im always getting and array with undefined:
array_to_be_sorted.map((objects) => {
array_reference.map((id) => {
if (objects.id === id) {
return {...objects}
}
}
}
im using map cause think is the best way for bigs array, because im building a music player, so dont know how many tracks the does the user has
You could use Array.prototype.sort() method to get the result.
const data = [
{ id: 2, name: 'carlos' },
{ id: 1, name: 'maria' },
{ id: 4, name: 'juan' },
{ id: 3, name: 'pepe' },
];
const order = [1, 2, 3, 4, 5];
data.sort((x, y) => order.indexOf(x.id) - order.indexOf(y.id));
console.log(data);
Another solution using Map Object which is faster than the first one.
const data = [
{ id: 2, name: 'carlos' },
{ id: 1, name: 'maria' },
{ id: 4, name: 'juan' },
{ id: 3, name: 'pepe' },
];
const order = [1, 2, 3, 4, 5];
const map = new Map();
order.forEach((x, i) => map.set(x, i));
data.sort((x, y) => map.get(x.id) - map.get(y.id));
console.log(data);
Why not just use Array.prototpye.sort()? It's easy and fast.
const pre = document.querySelector('pre');
let object = [
{id: 2, name: 'carlos'},
{id: 1, name: 'maria'},
{id: 4, name: 'juan'},
{id: 3, name: 'pepe'}
];
const criteria = [1,2,3,4,5];
pre.innerText = 'object:' + JSON.stringify(object, null, 2) + '\n\n';
object.sort((a, b) => {
return criteria[a.id] - criteria[b.id];
});
pre.innerText += 'sorted object:' + JSON.stringify(object, null, 2);
Sort an array using criteria from a second array:
<pre></pre>
You can take advantage of Schwartzian transform and sort data based on another array.
const data = [ { id: 2, name: 'carlos' }, { id: 1, name: 'maria' }, { id: 4, name: 'juan' }, { id: 3, name: 'pepe' }, ],
order = [4, 2, 3, 1, 5],
result = data.map(o => {
const index = order.indexOf(o.id);
return [index, o];
})
.sort((a, b) => a[0] - b[0])
.map(([, o]) => o);
console.log(result);

How to merge two array of objects when there is edit flag

I am having the array of objects like below
Array 1:
[{id: 1, name: 'Golden', isEdited: true}, {id: 2, name: 'Pearl'}]
Array 2:
[{id: 1, name: 'Golden'}, {id: 2, name: 'Pearlblue'}, , {id: 3, name: 'Orange'}]
Now i would like to merge the two arrays if the object contains isEdited flag means then that object should not be updated.
Expected result should be
[{id: 1, name: 'Golden', isEdited: true}, {id: 2, name: 'Pearlblue'}, {id: 3, name: 'Orange'}]
I have tried with the below approach
b.map((battr) => {
return {
...a[battr.id],
...battr
}
})
But it returns the output as
[{id: 1, name: 'Golden'}, {id: 2, name: 'Pearlblue'}, {id: 3, name: 'Orange'}]
Be carefull, your ids don't match your array indexes.
Hence in your attempt a[battr.id] should be replaced by something like a.find(a => a.id === battr.id)
Your example is confusing since in both arrays the name is 'Golden' for id=1, but assuming you want only the properties from array A if isEdited=true, your solution could be :
b.map((b_attr) => {
var matchingA = a.find(a_attr => a_attr.id === b_attr.id);
if (matchingA && matchingA.isEdited)
return matchingA;
else
return { ...matchingA, ...b_attr }
})

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

Javascript concat and overwrite where element has the same id

Is it possible to concat two arrays with objects and let the second array overwrite the first array where they have the same id:
// array 1
[
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
// array 2:
[
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
// out:
[
{id: 1, name: "newFoo"}, // overwriten by array 2
{id: 2, name: "bar"}, // not changed (from array 1)
{id: 3, name: "baz"}, // not changed (from array 1)
{id: 4, name: "y"}, // added (from array 2)
{id: 5, name: "z"} // added (from array 2)
]
If it is possible I would like to do this without the use of third party libraries
var a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var b = [
{id: 1, name: "fooboo"},
{id: 4, name: "bar"},
{id: 5, name: "baz"}
];
/* iterate through each of b, if match found in a, extend with that of a. else push into b ...*/
b.forEach(m => {
var item = a.find(n => n.id === m.id);
if(item) { return Object.assign(item, m); }
a.push(m);
});
console.log(a);
You can do
let arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
let arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
let result = arr1.concat(arr2).reduce((a, b) => {
a[b.id] = b.name;
return a;
},{})
result = Object.keys(result).map(e => {
return {id : e, name : result[e]};
});
console.log(result);
Explanation
I am using the property of objects that they don't keep duplicate keys, so for an array concated together, I reduce it to an object with id as it's key and name as its value, hence overriding all duplicates. In the next step I converted this back into an array.
Check you my solution. There is no "rewrite", i just use a second array as base and don't write value if it has same id.
let a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
let b = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
let duplicateId;
a.forEach(aitem => {
duplicateId = false;
b.forEach(bitem => {
if (aitem.id === bitem.id)
duplicateId = true;
});
if (!duplicateId)
b.push(aitem);
});
console.log(b);
Maybe you can use Object.assign and Object.entries to achieve, lets say:
const arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
const arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
const obj3 = Object.entries(Object.assign({}, ...arr1, arr2))
.map(([prop, value]) => ({[prop]:value}));
Example:
https://jsfiddle.net/0f75vLka/
Another option would be to convert arrays to map with id as key then merge the objects and then convert it back to array.
var arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
function arr2map(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
map[item.id] = item;
}
return map;
}
function map2arr(map) {
var arr = [];
for (var i in map) {
arr.push(map[i]);
}
return arr;
}
var arr1m = arr2map(arr1);
var arr2m = arr2map(arr2);
var arr3m = map2arr( Object.assign({}, arr1m, arr2m) );
//output
alert(JSON.stringify(arr3m));

Categories