Home > Mobile >  Solved - Is it possible to sort by values and also have some specific values appear first?
Solved - Is it possible to sort by values and also have some specific values appear first?

Time:12-22

Currently I have a table displaying and it is sorting by the first column alphabetically and then by the second if there are any duplicates in the first by using two sorts as below

  inventoryData = Object.values(inventoryData).sort((a, b) => {
                if (a.XI_MP_POS < b.XI_MP_POS) { return -1; }
                if (a.XI_MP_POS > b.XI_MP_POS) { return 1; }
                return 0;
            });
            inventoryData = Object.values(inventoryData).sort((a, b) => {
                if (a.XI_MP_DESC < b.XI_MP_DESC) { return -1; }
                if (a.XI_MP_DESC > b.XI_MP_DESC) { return 1; }
                return 0;
            });

It's been requested that this sort be maintained but for a few select XI_MP_DESC values to appear at the top regardless of their position alphabetically, is this possible?

I would like to turn for example:

Desc POS
A A
A B
A C
B A
C A
C B
C C

Into:

Desc POS
C A
C B
C C
A A
A B
A C
B A

By specifying that C always appear at the top

This has been solved using

const topDescriptions = ['desc1', 'desc2'];
              inventoryData = Object.values(inventoryData).sort((a, b) => {
                if (a.XI_MP_POS < b.XI_MP_POS) { return -1; }
                if (a.XI_MP_POS > b.XI_MP_POS) { return 1; }
                return 0;
            });
            inventoryData = Object.values(inventoryData).sort((a, b) => {
                if (a.XI_MP_DESC < b.XI_MP_DESC) { return -1; }
                if (a.XI_MP_DESC > b.XI_MP_DESC) { return 1; }
                return 0;
            });
            inventoryData = Object.values(inventoryData).sort((a, b) => {
                const aIsTop = topDescriptions.includes(a.XI_MP_DESC);
                const bIsTop = topDescriptions.includes(b.XI_MP_DESC);
                if (aIsTop && !bIsTop) return -1;
                if (bIsTop && !aIsTop) return 1;
                return 0;
            });

CodePudding user response:

You could take an object for the special order with a default value for unknown items:

  • zero for moving to top,
  • a large value for moving to bottom,
  • any other value for taking a custom sorting.

Inside of the single sorting function sort

  • Desc by order ascending,
  • Desc by string ascending.
  • POS by string ascending.

By using more than one function, the result is only predictable with a stable sorting algorithm.

const
    data = [{ Desc: 'A', POS: 'A' }, { Desc: 'A', POS: 'B' }, { Desc: 'A', POS: 'C' }, { Desc: 'B', POS: 'A' }, { Desc: 'C', POS: 'A' }, { Desc: 'C', POS: 'B' }, { Desc: 'C', POS: 'C' }],
    order = { C: 1, default: Number.MAX_VALUE };

data.sort((a, b) => 
    (order[a.Desc] || order.default) - (order[b.Desc] || order.default) ||
    a.Desc.localeCompare(b.Desc) ||
    a.POS.localeCompare(b.POS)
);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

You can use a third sort after the first two. See the snippet below. I also corrected two mistakes in your snippet: you cannot subtract strings (you'll get a NaN) and you should sort your list in place.

const inventoryData = [
 {XI_MP_POS: 1, XI_MP_DESC: 'aaa'},
 {XI_MP_POS: 4, XI_MP_DESC: 'bbb'},
 {XI_MP_POS: 3, XI_MP_DESC: 'ddd'},
 {XI_MP_POS: 3, XI_MP_DESC: 'ccc'},
];

const topDescriptions = ['bbb', 'aaa'];

inventoryData.sort((a, b) => a.XI_MP_DESC < b.XI_MP_DESC ? -1 : a.XI_MP_DESC > b.XI_MP_DESC ? 1 : 0);
inventoryData.sort((a, b) => a.XI_MP_POS - b.XI_MP_POS);
inventoryData.sort((a, b) => {
  const aIsTop = topDescriptions.includes(a.XI_MP_DESC);
  const bIsTop = topDescriptions.includes(b.XI_MP_DESC);
  if (aIsTop && !bIsTop) return -1;
  if (bIsTop && !aIsTop) return 1;
  return 0;
});

console.log(inventoryData)

  • Related