Home > Net >  Sort array of objects by property value within nested array of objects
Sort array of objects by property value within nested array of objects

Time:10-22

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>

  • Related