Home > Blockchain >  sorting a javascript array of objects by 2 values
sorting a javascript array of objects by 2 values

Time:12-13

I have an array of objects, and I want the selected object to always be first. Then, if the object has a role === External but it's not selected, I want that to always by last. If it doesn't match either, it's just in between the two.

const exampleObj = [
  { role: 'other', selected: false },
  { role: 'External', selected: false },
  { role: 'External', selected: false },
  { role: 'other', selected: true },
  { role: 'other', selected: false },
]

This works:

  exampleObj.sort((a, b) => {
        if (!a.selected && b.selected) return 1
        if (a.selected && !b.selected) return -1
        if (!a.role !== 'External' && b.role !== 'External') return 1
        if (a.role !== 'External' && !b.role !== 'External') return -1
        return 0

But I'm getting the prettier error

TS2367: This condition will always return 'true' since the types 'boolean' and 'string' have no overlap.
    108 |             if (!a.selected && b.selected) return 1
    109 |             if (a.selected && !b.selected) return -1
  > 110 |             if (!a.role !== 'External' && b.role !== 'External') return 1
        |                 ^^^^^^^^^^^^^^^^^^^^^^
    111 |             if (a.role !== 'External' && !b.role !== 'External') return -1
    112 |             return 0
    113 |           })

TS2367: This condition will always return 'true' since the types 'boolean' and 'string' have no overlap.
    109 |             if (a.selected && !b.selected) return -1
    110 |             if (!a.role !== 'External' && b.role !== 'External') return 1
  > 111 |             if (a.role !== 'External' && !b.role !== 'External') return -1
        |                                          ^^^^^^^^^^^^^^^^^^^^^^
    112 |             return 0
    113 |           })
    114 |         : []

But if I remove those 2 lines, it's obviously not going to push the External to the end. What am I missing here?

CodePudding user response:

You could use a.role === 'External' && b.role !== 'External' instead

!a.role !== 'External' is same as false !== 'External' because !a.role will be evaluated first and the result is compared to the string.

CodePudding user response:

You say !'string' which is always false. Because the exclamation mark is casting the variable (value) to boolean and negating it. So You actually say:

false !== 'string'

Which is always true. that's why it says "This condition will always return 'true'"

CodePudding user response:

The ! before a and b string variables are the problem. if (!a.role !== 'External' && b.role !== 'External') return 1 evaluates to "if (true !== 'External'.... Remove those ! for if (a.role !== 'External' && b.role !== 'External') return 1.

CodePudding user response:

You could take a more concise approach by using deltas of the conditions.

const
    array = [{ role: 'other', selected: false }, { role: 'External', selected: false }, { role: 'External', selected: false }, { role: 'other', selected: true }, { role: 'other', selected: false }];

array.sort((a, b) =>
    b.selected - a.selected ||
    (a.role === 'External' && !a.selected) - (b.role === 'External' && !b.selected)
);

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

  • Related