2

I create this array of objects:

const palette = [
  {
    color: 'Blue',
    brightness: 'Soft',
  },
  {
    color: 'Blue',
    brightness: 'Medium',
  },
  {
    color: 'Blue',
    brightness: 'Principal',
  },
  {
    color: 'Magenta',
    brightness: 'Soft',
  },
  {
    color: 'Magenta',
    brightness: 'Medium',
  },
  {
    color: 'Magenta',
    brightness: 'Principal',
  }
]

I want a new array with objects in this order:

const colorOrder = ['Blue', 'Magenta']
const brightnessOrder = ['Principal', 'Soft', 'Medium']

So this is the result I would like to have:

const colors = [
  {
    color: 'Blue',
    brightness: 'Principal',
  },
  {
    color: 'Blue',
    brightness: 'Soft',
  },
  {
    color: 'Blue',
    brightness: 'Medium',
  },
  {
    color: 'Magenta',
    brightness: 'Principal',
  }
  {
    color: 'Magenta',
    brightness: 'Soft',
  },
  {
    color: 'Magenta',
    brightness: 'Medium',
  },
]

I try this function:

function sortArrayByAnotherArray(array: any[], order: number[] | string[], key: string) {
  const newArray = array.slice(0).sort((a, b) => {
    const A = a[key]
    const B = b[key]
    return order.indexOf(A) < order.indexOf(B) ? 1 : -1
  })
  return newArray
}

I call it in this way:

const palette1 = sortArrayByAnotherArray(
  palette,
  brightnessOrder,
  'brightness'
)
const palette2 = sortArrayByAnotherArray(
  palette1,
  colorOrder,
  'color'
)

console.log('\n', palette)

console.log('\n', brightnessOrder)
console.log(palette1)

console.log('\n', colorOrder)
console.log(palette2)

The result is:

`
` [ { color: 'Blue', brightness: 'Soft' },
  { color: 'Blue', brightness: 'Medium' },
  { color: 'Blue', brightness: 'Principal' },
  { color: 'Magenta', brightness: 'Soft' },
  { color: 'Magenta', brightness: 'Medium' },
  { color: 'Magenta', brightness: 'Principal' } ]
`
` [ 'Principal', 'Soft', 'Medium' ]
[ { color: 'Blue', brightness: 'Medium' },
  { color: 'Magenta', brightness: 'Medium' },
  { color: 'Blue', brightness: 'Soft' },
  { color: 'Magenta', brightness: 'Soft' },
  { color: 'Blue', brightness: 'Principal' },
  { color: 'Magenta', brightness: 'Principal' } ]
`
` [ 'Blue', 'Magenta' ]
[ { color: 'Magenta', brightness: 'Medium' },
  { color: 'Magenta', brightness: 'Soft' },
  { color: 'Magenta', brightness: 'Principal' },
  { color: 'Blue', brightness: 'Medium' },
  { color: 'Blue', brightness: 'Soft' },
  { color: 'Blue', brightness: 'Principal' } ]

It's a mess, the order is not like the one in the arrays: colors are inverted and also brightness values. Then I think that call this function twice (or more) creates problems. Is there a way to solve this? Exists a smarted way to do what I need?

1
  • what about unknown values in the order arrays? do you have a complete list, or should unknown items moved to top or bottom or at any other place? Commented Aug 2, 2019 at 9:29

3 Answers 3

1

You could chain the wanted order with logical OR || and the deltas of the indices.

const
    palette = [{ color: 'Blue', brightness: 'Soft' }, { color: 'Blue', brightness: 'Medium' }, { color: 'Blue', brightness: 'Principal' }, { color: 'Magenta', brightness: 'Soft' }, { color: 'Magenta', brightness: 'Medium' }, { color: 'Magenta', brightness: 'Principal' }],
    colorOrder = ['Blue', 'Magenta'],
    brightnessOrder = ['Principal', 'Soft', 'Medium'];

palette.sort((a, b) => 
    colorOrder.indexOf(a.color) - colorOrder.indexOf(b.color) ||
    brightnessOrder.indexOf(a.brightness) - brightnessOrder.indexOf(b.brightness)
);

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

An approach to sort a copy with a function and given order arrays.

function sortArrayByAnotherArrays(data, orders) {
    const
        getObject = array => array.reduce((r, k, i) => (r[k] = i + 1, r), {}),
        objects = orders.map(([k, a]) => [k, getObject(a)]);

    return data
        .slice()
        .sort((a, b) => {
            var v;
            objects.some(([k, o]) => v = o[a[k]] - o[b[k]]);
            return v;
        });
}

const
    palette = [{ color: 'Blue', brightness: 'Soft' }, { color: 'Blue', brightness: 'Medium' }, { color: 'Blue', brightness: 'Principal' }, { color: 'Magenta', brightness: 'Soft' }, { color: 'Magenta', brightness: 'Medium' }, { color: 'Magenta', brightness: 'Principal' }],
    colorOrder = ['Blue', 'Magenta'],
    brightnessOrder = ['Principal', 'Soft', 'Medium'],
    ordered = sortArrayByAnotherArrays(
        palette,
        [
            ['color', colorOrder],           // [key, values in order]
            ['brightness', brightnessOrder]
        ]
    );

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

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! I try to generalize this process creating this function: function sortBy(datasetToSort: any[], orders: {order: string[] | number[], key: string}[]) { const datasetToSortCloned = datasetToSort.slice(0) const result = datasetToSortCloned.sort((a, b) => { const something = orders.map((o, i) => { const {order, key} = o return order.indexOf(a['key']) - order.indexOf(b['key']) }) // something[0] || something[1] || ... }) return result } But how can I do something[0] || something[1] || ...?
Thanks but it seems not to work. Look here: codesandbox.io/s/admiring-rgb-4y2tn
the key name of the sort must match the given name in the object. instead of color, you need colorName, otherwise the property can not be addressed properly.
0

If the result is inverted, you can try to invert the sort condition

return order.indexOf(A) > order.indexOf(B) ? 1 : -1

Comments

0

Sort by the difference in index of each objects' color in the colorOrder, and alternate with the difference in index of each objects' brightness in the brightnessOrder.

Keep in mind that .sort sorts in place - sorted is the same object as palette. If you don't want to mutate the original array, clone it first.

const palette=[{color:"Blue",brightness:"Soft"},{color:"Blue",brightness:"Medium"},{color:"Blue",brightness:"Principal"},{color:"Magenta",brightness:"Soft"},{color:"Magenta",brightness:"Medium"},{color:"Magenta",brightness:"Principal"}];

const colorOrder = ['Blue', 'Magenta'];
const brightnessOrder = ['Principal', 'Soft', 'Medium'];

const sorted = palette.sort((a, b) => (
  colorOrder.indexOf(a.color) - colorOrder.indexOf(b.color) ||
  brightnessOrder.indexOf(a.brightness) - brightnessOrder.indexOf(a.brightness)
));
console.log(sorted);

Comments