I'm trying to figure out how to sort an array of objects by whether or not the value of a property in a nested array of objects contain the value stopped
. When that value exists in any nested array of object, I need the parent object to be sorted to the top, from there, I'm trying to secondarily sort that sorted list by id
.
const arr = [
{
id: 1,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
},
{
id: 2,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'stopped',
},
],
},
{
id: 3,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
}
]
// desired result
[
{
id: 2,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'stopped',
},
],
},
{
id: 1,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
},
{
id: 3,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
}
]
CodePudding user response:
arr.sort((a, b) => {
if (a.things.some(thing => thing.status === "stopped")) {
return -1;
} else {
return a.id - b.id;
}
});
You simply have to sort, checking that the current object being inspected has at least one "thing" with a status "stopped", otherwise a normal numerical order.
CodePudding user response:
arr.sort((a, b) => {
const stoppeds_in_a = a.things.map(obj => obj.status).filter(status => status === 'stopped').length
const stoppeds_in_b = b.things.map(obj => obj.status).filter(status => status === 'stopped').length
// I want that who has more 'stoppeds' occurrences first
return stoppeds_in_b - stoppeds_in_a
})
CodePudding user response:
const checkStopped = (things) => things.some((el) => el.status === 'stopped');
const desired = arr.sort((a, b) => checkStopped(b.things) - checkStopped(a.things));
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
You could introduces a helper function that partitions the collection based on a provided callback. Then concatenate them together to create the desired result.
const arr = [{id:1,things:[{thing_id:1,status:'started'},{thing_id:1,status:'started'}]},{id:2,things:[{thing_id:1,status:'started'},{thing_id:1,status:'started'},{thing_id:1,status:'stopped'}]},{id:3,things:[{thing_id:1,status:'started'},{thing_id:1,status:'started'},{thing_id:1,status:'started'},{thing_id:1,status:'started'}]}];
const [withStopped, withoutStopped] = partition(arr,
item => item.things.some(item => item.status == "stopped")
);
const result = withStopped.concat(withoutStopped);
console.log(result);
// helper
function partition(iterable, fn) {
const partitions = { "true": [], "false": [] };
for (const item of iterable) partitions[!!fn(item)].push(item);
return [partitions[true], partitions[false]];
}
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
This is actually a good task for recursion. But if the structure is fixed, I took two loops and one condition. If this is true I made an array push into the global res variable.
const arr = [
{
id: 1,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
},
{
id: 2,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'stopped',
},
],
},
{
id: 3,
things: [
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
{
thing_id: 1,
status: 'started',
},
],
}
]
const res = [];
arr.forEach(function(e) {
let val = Object.values(e.things)
val.forEach((t) => {
if(t.status == "stopped") {
res.push(e)
}
})
})
console.log('res', res)
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>