Emilio Contreras
Emilio Contreras

Reputation: 41

How to order an array of objects by value? usi

I have an array of objects such as

const myArrayOfColors = [
  { color: "red", number: 123 },
  { color: "red", number: 12 },
  { color: "green", number: 6 },
  { color: "blue", number: 7 },
  { color: "purple", number: 54 },
  { color: "green", number: 74 },
  { color: "blue", number: 41 },
  { color: "purple", number: 74 },
];

and I have an array of strings that I have to use as an order reference:

myOrder = ["red", "blue", "purple", "green"];

so I have to sort my objects array in order to comply with my order reference. I should have an output like this:

const myArrayOfColors = [
  { color: "red", number: 123 },
  { color: "blue", number: 7 },
  { color: "purple", number: 54 },
  { color: "green", number: 6 },
  { color: "red", number: 12 },
  { color: "blue", number: 41 },
  { color: "purple", number: 74 },
  { color: "green", number: 74 },
];

Upvotes: 1

Views: 98

Answers (5)

loop
loop

Reputation: 973

First build separate lists of the entries for each color, then use a custom comparator function for Array.prototype.sort().

const myArrayOfColors = [
  { color: "red", number: 123 },
  { color: "red", number: 12 },
  { color: "green", number: 6 },
  { color: "blue", number: 7 },
  { color: "purple", number: 54 },
  { color: "green", number: 74 },
  { color: "blue", number: 41 },
  { color: "purple", number: 74 },
];

myOrder = ["red", "blue", "purple", "green"];

myArraysPerColor = {};
for(const color of myOrder) {
  myArraysPerColor[color] = myArrayOfColors.filter(v=>v.color===color);
}

myArrayOfColors.sort((a,b) => 
  myArraysPerColor[a.color].indexOf(a)-myArraysPerColor[b.color].indexOf(b) ||
    myOrder.indexOf(a.color)-myOrder.indexOf(b.color)
);


console.log(myArrayOfColors);

Upvotes: 0

ulou
ulou

Reputation: 5853

Will work even for uneven groups that cannot be repeated.

I've added {color:'green',number:74} and {color:'red',number:74} to the end of myArrayOfColors:

const myArrayOfColors=[{color:'red',number:123},{color:'red',number:12},{color:'green',number:6},{color:'blue',number:7},{color:'purple',number:54},{color:'green',number:74},{color:'blue',number:41},{color:'purple',number:74},{color:'green',number:74}, {color:'red',number:74}]
const myOrder = ['red', 'blue', 'purple', 'green']

const res = []
let maxIterations = myArrayOfColors.length - myOrder.length

while (maxIterations--) {
  myOrder.forEach(e => {
    const c = myArrayOfColors.find(x => x.color === e) ?? {}
    c.color && res.push({...c})
    c.color = "-"
  })
}

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

Upvotes: 0

DecPK
DecPK

Reputation: 25408

  • You can easily achieve this result using first create a dict that contains the color and its number in the respective array.
  • calculate the max number of elements in respective color.
  • Now we know the max elements in an array, so we can loop over up to max and get color and its number from dict and store in the result array.`

const myArrayOfColors = [
  { color: "red", number: 123 },
  { color: "red", number: 12 },
  { color: "green", number: 6 },
  { color: "blue", number: 7 },
  { color: "purple", number: 54 },
  { color: "green", number: 74 },
  { color: "blue", number: 41 },
  { color: "purple", number: 74 },
  { color: "purple", number: 94 },
];

const myOrder = ["red", "blue", "purple", "green"];
let max = 0;
const dict = myArrayOfColors.reduce((acc, curr) => {
  const { color, number } = curr;
  if (acc[color]) acc[color].push(number);
  else acc[color] = [number];

  max = Math.max(max, acc[color].length);
  return acc;
}, {});

const result = [];
for (let i = 0; i < max; ++i) {
  myOrder.forEach((orderKey) => {
    const { [i]: val } = dict[orderKey];
    if (val) result.push({ color: orderKey, number: val });
  });
}

console.log(result);

Above snippet can also cover the case if any color repeat more than in myArrayOfColors, e.g purple repeat more than any color in array

const myArrayOfColors = [
  { color: "red", number: 123 },
  { color: "red", number: 12 },
  { color: "green", number: 6 },
  { color: "blue", number: 7 },
  { color: "purple", number: 54 },
  { color: "green", number: 74 },
  { color: "blue", number: 41 },
  { color: "purple", number: 74 },
  { color: "purple", number: 94 },
  { color: "purple", number: 84 },
  { color: "purple", number: 64 },
  { color: "purple", number: 54 },
];

Upvotes: 0

Naren
Naren

Reputation: 4470

Not sure this is best way, You can try this. So basically we run through the myArrayOfColors and then loop the order to find the order element, push it in result, then after Delete the item from main source (myArrayOfColors). Of course think about Time Complexity (assuming n³) here. (It's big 😶)

const myArrayOfColors = [
  {color: 'red', number: 123},
  {color: 'red',  number: 12},
  {color:'green', number: 6},
  {color: 'blue', number: 7},
  {color:'purple', number:54},
  {color: 'green', number: 74},
  {color:'blue', number:41},
  {color: 'purple', number: 74},
]

const order = ['red', 'blue', 'purple', 'green']
const result = []

while(myArrayOfColors.length) {
 for (const o of order) {
    const colorIndex = myArrayOfColors.findIndex(e => e.color === o)
    if (colorIndex > -1) {
      result.push(myArrayOfColors[colorIndex])
      myArrayOfColors.splice(colorIndex, 1)
    }
  }
}

console.log(result)

Upvotes: 1

Scotty Jamison
Scotty Jamison

Reputation: 13129

Here's a simple way to make groups of unique elements, sorted according to a colors array:

function* createdOrderedGroups(items, colors) {
  items = [...items]
  let colorIndex = 0;
  while (items.length) {
    const index = items.findIndex(item => item.color === colors[colorIndex])
    if (index === -1) {
      throw new Error('Each color did not have the same number of assosiated items.');
    }
    yield items[index]
    items.splice(index, 1);

    colorIndex = (colorIndex + 1) % colors.length
  }
}

const myArrayOfColors = [
  {color: 'red', number: 123},
  {color: 'red',  number: 12},
  {color:'green', number: 6},
  {color: 'blue', number: 7},
  {color:'purple', number:54},
  {color: 'green', number: 74},
  {color:'blue', number:41},
  {color: 'purple', number: 74},
]

console.log([...createdOrderedGroups(myArrayOfColors, ['red', 'blue', 'purple', 'green'])])

Basically, we gradually remove items from the input array every time we find one with the color we're looking for, until there are no items left. We know which color we're looking for, by using a colorIndex variable, which starts at 0, and increments with each iteration until we get to the length of the color array, then it restarts at 0.

I'm using a generator here (the function* and yield stuff), but if that's new to you, you can instead just build a results array and return that whole array.

This solution will yield the first occurrence of each color from the input array, then it will yield the next occurrence it finds, and so forth, so the last item that has a color "red" will be one of the last elements yielded.

Upvotes: 0

Related Questions