Sort grouped arrays by priority

Viewed 143

Sorry for the bad wording, I can't even formulate it in an amicable way, so I'll describe it with examples.

I have a scope of priorities:

const priorities = new Map([
    ['lowest', 1],
    ['low', 2],
    ['high', 3],
    ['highest', 4],
])

I have an array of objects with groups and priorities:

const array = [
    {id: 1, priority: 'high'},
    {id: 2, priority: 'lowest'},
    {id: 3, priority: 'highest'},
    {id: 4, group: 1, priority: 'low'},
    {id: 5, group: 2, priority: 'low'},
    {id: 6, group: 2, priority: 'lowest'},
    {id: 7, group: 2, priority: 'low'},
    {id: 8, group: 1, priority: 'high'}
];

And I have grouped this array to an object with grouped arrays of elements:

const groups = {
    0: [{ id: 1, priority: 'high' }, { id: 2, priority: 'lowest' }, { id: 3, priority: 'highest' }], // have no highest priority for group 0
    1: [{ id: 4, group: 1, priority: 'low' }, { id: 8, group: 1, priority: 'high' }], // high
    2: [{ id: 5, group: 2, priority: 'low' }, { id: 6, group: 2, priority: 'lowest' }, { id: 7, group: 2, priority: 'low' }] // low
};

The priority of the group (except group 0) it's highest priority of the items in this group. I need to pick highest priority from an each group (except group 0) to sort groups and items from group 0 from highest to lowest, it should looks like

[<highest by priority item w/o group>, ...<items from highest by priority group>, ... , <lowest by priority items>, <>]

At all, I need sort by group's priority + item without group priority + initial position. E.g. result for the current initial array:

const result = [
    { id: 3, priority: 'highest' },
    { id: 1, priority: 'high' },
    { id: 4, group: 1, priority: 'low' },
    { id: 8, group: 1, priority: 'high' },
    { id: 5, group: 2, priority: 'low' },
    { id: 6, group: 2, priority: 'lowest' },
    { id: 7, group: 2, priority: 'low' },
    { id: 2, priority: 'lowest' },
];

How I could make it?

1 Answers

You can

  • give each item an index, to know their original order. (It might also be possible to rely on sort stability, but it's easier to reason about when being explicit):

    for (const [i, o] of array.entries()) o.index = i;
    
  • give each group a key to sort on, which is based on the max priority and first item:

    const groupIds = Object.keys(groups).filter(g => g != 0);
    groupIds.map(g => {
      let minIndex = Infinity, maxPriority = 0;
      for (const item of groups[g])
        minIndex = Math.min(minIndex, item.index);
        maxPriority = Math.max(maxPriority, priorities.get(item.priority));
      }
      return {group: groups[g], minIndex, maxPriority};
    })
    
  • add one single-item "group" per ungrouped item:

    ….concat(groups[0].map(item => {
      return {group: [item], maxPriority: priorities.get(item.priority), minIndex: item.index};
    }))
    
  • before sorting the array of groups:

    ….sort((a, b) => b.maxPriority - a.maxPriority || a.minIndex - b.minIndex)
    
  • and then joining them together into the result:

    ….flatMap(({group}) => {
      return group.sort((a, b) => a.index - b.index); // might be unnecessary depending on how you built the group arrays
    });
    
Related