Sort array of objects by property value within nested array of objects

Viewed 322

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',
            },
        ],
    }
]
6 Answers
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.

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
})

const checkStopped = (things) => things.some((el) => el.status === 'stopped');

const desired = arr.sort((a, b) => checkStopped(b.things) - checkStopped(a.things));

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]];
}

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)

you should do something like this

const Firstresult = [];//with stopped
const SecontPartresult = [];//without stopped

arr.forEach(element => {
    element.things.forEach(thing => {
        if (thing.status == "stopped") {
            if (Firstresult.filter(x => x.id == element.id).length == 0) {
                Firstresult.push(element)
            }
        } else if (element.things.filter(x => x.status == "stopped").length === 0) {
            if (SecontPartresult.filter(x => x.id == element.id && x.things !== "stopped").length == 0) {
                SecontPartresult.push(element)
            }
        }

    });

});

SecontPartresult.forEach(element => {
    Firstresult.push(element)
});
console.table(Firstresult)
Related