Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort by defined values, if found

Using native ES2015 only (i.e. no libraries) I need to sort a list of objects by a defined set of values (think T-shirt size) AND prioritize the ones that match one of the defined values.

Sample data:

const shirt_stock= [
    {size: "M"},
    {size: "L"},
    {size: "M"},
    {size: "XM"},
    {size: "XL"} ]

Defined sort order:

const sort_order = ["S", "M", "L", "XL"]

Sort code (no comments needed on use of indexOf() in sort, it's pseudo code):

shirt_stock.sort((a, b) => {
  return sort_order.indexOf(b.size) - sort_order.indexOf(a.size)
})

Yields result:

> 0: "XM"
> 1: "M"
> 2: "M"
> 3: "L"
> 4: "XL"

The problem is that first result, "XM" - I want that to be at the end. In other words, I want to sort shirts that have a known size first, in the order defined in sort_order, then everything else after - bonus if the "unknown size shirts" are also sorted so they're grouped by size.

Desired result:

> 0: "M"
> 1: "M"
> 2: "L"
> 3: "XL"
> 4: "XM"

I'm stumped, what's a clean (e.g. not splitting shirt_stock into two arrays, sorting and combing back) and reasonably efficient way to achieve this?

like image 294
scubasteve Avatar asked Feb 15 '26 16:02

scubasteve


1 Answers

indexOf return -1 for elements that are not found, which are then put first. You will need to use a value that's higher than all the other indices for those elements. Here's a quick way to achieve that:

shirt_stock.sort((a, b) => {
  return (sort_order.indexOf(a.size)+1 || sort_order.length+1) - (sort_order.indexOf(b.size)+1 || sort_order.length+1)
})

Since you already plan on not using indexOf directly in the callback, you'll probably find a more elegant way to incorporate these default values as well :-)

like image 93
Bergi Avatar answered Feb 17 '26 06:02

Bergi