1
const modules = [
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
]

I am trying to sort the array so that the True values come first , then False values.

const orderedModules = modules.sort((a, b) => (a.checked ? -1 : 1))

However, I'd like to preserve the order of the True values. The code above sometimes puts Air first, then Earth (if ran twice). How can I preserve the order all the time?

TIMEX
  • 259,804
  • 351
  • 777
  • 1,080

4 Answers4

1

If you don't mind creating a new array, just iterate over the array twice. The first time, push to the new array the objects with the true values as the iteration encounters them. The second time, push the objects with the false values. (JavaScript passes objects by reference so the new array won't cause them to get duplicated.)

גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
1

The callback used as the compare function can return 3 options: negative number, positive number or zero. The negative number indicates that the first parameter should be before the second parameter in the array order. The positive number indicates that the first parameter should be after the second parameter in the array order. And zero means that the order should be kept as is.

Sort array method on MDN

If you want to order just the true values first in the same order in the array and then the false values, probably adding more logic to return zero from the compare function if both are true will solve your issue.

Here is an example:

const modules = [
  { name: 'Wood', checked: false },
  { name: 'Metal', checked: false },
  { name: 'Earth', checked: true },
  { name: 'Water', checked: false },
  { name: 'Air', checked: true },
  { name: 'Fire', checked: false },
];
modules.sort((a,b) => a.checked && b.checked ? 0 : a.checked ? -1 : 1);
console.log(modules);
Zion Ay
  • 191
  • 4
  • [Note: the ECMAScript standard only started guaranteeing this behavior in 2019, thus, older browsers may not respect this.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description) – גלעד ברקן May 08 '21 at 22:33
0

the reason is that sort actually changes the original array. Although modules is defined with const, the values inside can change, as long as you don't assign the variable to something else.

according to this answer, you can sort without mutating the original using spread syntax. this code should work:

const orderedModules = [...modules].sort((a, b) => (a.checked ? -1 : 1))

to make an array or object not able to be modified, you can use Object.freeze()

const modules = Object.freeze([
    { name: 'Wood', checked: false },
    { name: 'Metal', checked: false },
    { name: 'Earth', checked: true },
    { name: 'Water', checked: false },
    { name: 'Air', checked: true },
    { name: 'Fire', checked: false },
])

Edit: I just realized the order isn't correct, but it at least is the same every time. but that's because the sorting isn't exactly right. here's the correct code:

const orderedModules = [...modules].sort((a, b) => (a.checked != b.checked ? (a.checked ? -1 : 1 ) : 0))
Tony Zhang
  • 417
  • 4
  • 8
  • I don't think OP is concerned about changing the order of elements in the original array. I think they are actively trying to change it. They want to keep the existing order just within the group of objects with true values. – גלעד ברקן May 08 '21 at 20:28
  • the reason that the order is changing is BECAUSE the original is changing. sort isn't random, but when the array changes, sort produces a different output for whatever reason – Tony Zhang May 08 '21 at 20:29
0

Probably this might help you. not sure for optimize way but this function iterate over an array one time only.

I am using reduce function to separate out true and false values and then return them in the order you want.

const shortItems = (array) => {
    const orderedModulesObject = array.reduce((orderedModulesObject, currentModule) => {
            if(currentModule.checked){
                orderedModulesObject.trueValues = orderedModulesObject.trueValues.concat(currentModule);
            } else {
                orderedModulesObject.falseValues = orderedModulesObject.falseValues.concat(currentModule);
            }
            return orderedModulesObject;
        
    }, { trueValues: [], falseValues: []});
    return orderedModulesObject.trueValues.concat(orderedModulesObject.falseValues);
}

const modules = [
{ name: 'Wood', checked: false },
{ name: 'Metal', checked: false },
{ name: 'Earth', checked: true },
{ name: 'Water', checked: false },
{ name: 'Air', checked: true },
{ name: 'Fire', checked: false },
]

console.log(shortItems(modules));
Tony Zhang
  • 417
  • 4
  • 8
Raj Thakar
  • 198
  • 9
  • if you don't want to create new function see this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability – Raj Thakar May 08 '21 at 20:35