Related
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);
I'm trying to dynamically build Objects and push them one by one into an Array.
My code so far is this:
matrix([[0,1,1], [1,00], [1,1,1])
const matrix = (sequence) => {
const rows = {}
const rowsAry = []
let row
let idx
for (let i=0; i < samples.length; i++) {
row = `row${i}`
for (let j=0; j < samples[i].length; j++) {
if (samples[i][j] === 1) {
idx = []
rows[row] = rows[row] + 1 || 1
rows['indeces'] = idx.push(j)
rowsAry.push(rows)
}
}
}
console.log(rowsAry)
}
This obviously isn't working. I'm trying to map out all the '1's' in the sequence and know how many are in a row and what there indices are.
The wrong out put is:
[ { row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 },
{ row0: 3, indeces: 1, row1: 2, row2: 2 } ]
Hoped for output would be:
[{row1: 2, indices: [1,2]},
{row2: 1, indices: [0]},
{row3: 3, indices: [0,1,2]}
]
You need to create a new object each time through the outer loop. And only push it onto rowsAry once, not each time through the inner loop.
const matrix = (samples) => {
const rowsAry = []
for (let i = 0; i < samples.length; i++) {
let row = `row${i}`
let indices = [];
for (let j = 0; j < samples[i].length; j++) {
if (samples[i][j] === 1) {
indices.push(j);
}
}
rowsAry.push({[row]: indices.length, indices: indices});
}
console.log(rowsAry)
}
matrix([
[0, 1, 1],
[1, 00],
[1, 1, 1]
])
You can solve it by simply using array map and forEach method. Traverse the array of arrays and check for ones to the inside array by using array forEach method and if found put it to the resultant array. At last, using the resultant array make your required object.
const matrix = (sequence) => {
return sequence.map((x, i) => {
const ret = [];
x.forEach((y, j) => {
if (y === 1) ret.push(j);
});
const key = `row${i + 1}`;
const obj = { [key]: ret.length, indices: ret };
return obj;
});
};
const ret = matrix([
[0, 1, 1],
[1, 0, 0],
[1, 1, 1],
]);
console.log(ret);
Or nested reduce() calls...
const matrix = (sequence) => {
return sequence.reduce((a, r, i) => {
indices = r.reduce((ra, rn, ri) => {
if (rn === 1) ra.push(ri);
return ra;
}, []);
a.push({[`row${i}`]: indices.length, indices: indices});
return a;
}, []);
}
console.log(matrix([[0,1,1], [1,0,0], [1,1,1]]))
But in reference to Barmar's comment about using different rowX keys a more accessible shape for your objects might look something along the lines of
{row: 1, count: 2, indices: [1, 2]}
I guess I have a dead simple problem but still didn't find a solution. I have an array which looks like this:
var originalArray = [{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}]
I'd like to modify it to look like this (merge by id and join elements):
newArray = [{
id: 1,
elements: [1, 2, 3, 4]
}, {
id: 5,
elements: ['a', 'b', 'c', 'd']
}, {
id: 27,
elements: []
}]
I already had multiple tries but still didn't find an elegant way of doing it.
You can create an object keyed by ID and push elements with the same ID to them, then convert back to an array. This is more efficient than looping through on every iteration for larger arrays:
var originalArray = [{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}];
const arrayHashmap = originalArray.reduce((obj, item) => {
obj[item.id] ? obj[item.id].elements.push(...item.elements) : (obj[item.id] = { ...item });
return obj;
}, {});
const mergedArray = Object.values(arrayHashmap);
console.log(mergedArray);
Try this code :
var originalArray = [{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}]
var newArray = [];
originalArray.forEach(item => {
var newItem = {id: item.id, elements: []};
originalArray.forEach(innerItem => {
if(innerItem.id == item.id){
newItem.elements = newItem.elements.concat(innerItem.elements);
}
});
newArray.push(newItem);
});
console.log(newArray);
Output :
[{
id: 1,
elements: [1, 2, 3, 4]
}, {
id: 5,
elements: ['a', 'b', 'c', 'd']
}, {
id: 27,
elements: []
}]
You can use Array.prototype.reduce() to create an object with the ids as properties and the Object.values() to get the result:
const originalArray = [{id: 1, elements: [1, 2]}, {id: 1, elements: [3, 4]}, {id: 5, elements: ['a', 'b']}, {id: 5, elements: ['c', 'd']}, {id: 27, elements: []}]
const objIds = originalArray.reduce((a, { id, elements }) => {
a[id] = a[id] || {id, elements: []}
return {...a, ...{[id]: {id, elements: a[id].elements.concat(elements)}}}
}, {})
const result = Object.values(objIds)
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do this with a reduce function:
[{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}].reduce((prev, cur) => {
const index = prev.findIndex(v => v.id === cur.id);
if (index === -1) {
prev.push(cur);
} else {
prev[index].elements.push(...cur.elements);
}
return prev;
}, [])
This will work and is also decently easy to understand.
Firstly we check if the id is already in the newArray or not and we keep memory of this through a boolean outside the loop that we can verify later on.
After this, if the id "space" is empty, we will fill it up, if it isn't then there is already an id there.
Therefore, we need to update their elements. We can do this by firstly, grabbing the object in the new array that corresponds with the duplicate object in the initial array which has the same id.
After this, we simply push each element from the duplicate to the new one.
var originalArray = [{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}]
var newArray = [];
for (obj of originalArray) {
var empty = true;
for (newobj of newArray) {
if (obj.id == newobj.id) { empty = false; }
}
if (empty) {
newArray.push({id: obj.id, elements: obj.elements});
} else {
for (newobj of newArray) {
if (newobj.id == obj.id) {
for (o of obj.elements) {
newobj.elements.push(o);
}
}
}
}
}
console.log(newArray);
You could do this using reduce method and Map to store unique values for each id and then create an array using spread syntax ....
var data = [{"id":1,"elements":[1,2]},{"id":1,"elements":[3,4]},{"id":5,"elements":["a","b"]},{"id":5,"elements":["c","d"]},{"id":27,"elements":[]}]
const result = data.reduce((r, {id, elements}) => {
if(r.get(id)) r.get(id).elements.push(...elements);
else r.set(id, {id, elements});
return r;
}, new Map).values();
console.log([...result])
You can use nested for loops to do that.
var originalArray = [{
id: 1,
elements: [1, 2]
},
{
id: 1,
elements: [3, 4]
},
{
id: 5,
elements: ['a', 'b']
},
{
id: 5,
elements: ['c', 'd']
}, {
id: 27,
elements: []
}]
for(let i=0;i<originalArray.length;i++){
let key = originalArray[i].id;
for(let j=i+1;j<originalArray.length;j++){
if(originalArray[j].id == key){
originalArray[i].elements = [...originalArray[i].elements,...originalArray[j].elements];
delete originalArray.splice(j,1);
}
}
}
console.log(originalArray);
This question already has answers here:
How do I sort an array of objects based on the ordering of another array?
(9 answers)
Javascript - sort array based on another array
(26 answers)
Closed 4 years ago.
I have two arrays.
itemsArray =
[
{ id: 8, name: 'o'},
{ id: 7, name: 'g'},
{ id: 6, name: 'a'},
{ id: 5, name: 'k'},
{ id: 4, name: 'c'}
]
sortArray = [4,5]
How can i sort itemsArray by sortArray (lodash or pure), but i want to for this:
newArray =
[
{ id: 4, name: 'c'},
{ id: 5, name: 'k'},
{ id: 8, name: 'o'},
{ id: 7, name: 'g'},
{ id: 6, name: 'a'}
]
In a case like this where you want to sort on multiple levels, you need to sort them in descending order of importance inside your sorting function.
In this case we sort regularly on cases where both elements are either in or not in the sorting array.
var itemsArray = [
{ id: 8, name: 'o' },
{ id: 7, name: 'g' },
{ id: 6, name: 'a' },
{ id: 5, name: 'k' },
{ id: 4, name: 'c' }
];
var sortArray = [4, 5];
var sortedItemsArray = itemsArray.sort(function (a, b) {
if (sortArray.includes(a.id) == sortArray.includes(b.id)) { //both or neither are in sort array
return b.id - a.id;
}
else if (sortArray.includes(a.id)) { //only a in sort array
return -1;
}
else { //only b in sort array
return 1;
}
});
console.log(sortedItemsArray);
The above snippet could be expanded in multiple ways, but a popular approach is to separate it into several sorting steps.
var itemsArray = [
{ id: 8, name: 'o' },
{ id: 7, name: 'g' },
{ id: 6, name: 'a' },
{ id: 5, name: 'k' },
{ id: 4, name: 'c' }
];
var sortArray = [4, 5];
function sortId(a, b) {
return b.id - a.id;
}
function sortIdByList(a, b) {
if (sortArray.includes(a.id)) {
return -1;
}
if (sortArray.includes(b.id)) {
return 1;
}
return 0;
}
//TEST
var sortedItemsArray = itemsArray
.sort(sortId)
.sort(sortIdByList);
console.log(sortedItemsArray);
This pattern can be easier to maintain as each step is clearly labeled and the functions can be reused in other sorting cases.
The only downside to this pattern is that you end up iterating over the list multiple times, thus increasing the time to sort. Usually this is a non-issue but on very large lists this can be significant.
Sort by array index only
As the comments points out i misread the question, so my previous two sorting snippets doesn't necessarily give the desired result.
This version sorts only by id index in the sorting array:
var itemsArray = [
{ id: 8, name: 'o' },
{ id: 7, name: 'g' },
{ id: 6, name: 'a' },
{ id: 5, name: 'k' },
{ id: 4, name: 'c' }
];
var sortArray = [4, 5];
//TEST
var sortedItemsArray = itemsArray
.sort(function (a, b) {
//Calculate index value of a
var A = sortArray.indexOf(a.id);
if (A == -1) {
A = sortArray.length;
}
//Calculate index value of b
var B = sortArray.indexOf(b.id);
if (B == -1) {
B = sortArray.length;
}
//Return comparison
return A - B;
});
console.log(sortedItemsArray);
You could take the indices of the array for keeping the relative position and take the special items with a negative index to top for sorting.
Then sort the array by taking the indices.
var array = [{ id: 8, name: 'o' }, { id: 7, name: 'g' }, { id: 6, name: 'a' }, { id: 5, name: 'k' }, { id: 4, name: 'c' }],
sortArray = [4, 5],
indices = array.reduce((r, { id }, i) => (r[id] = i, r), {});
sortArray.forEach((id, i, { length }) => indices[id] = i - length);
array.sort(({ id: a }, { id: b }) => indices[a] - indices[b]);
console.log(array);
console.log(indices);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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];
});