Reputation: 271614
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?
Upvotes: 2
Views: 1855
Reputation: 427
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))
Upvotes: 0
Reputation: 301
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.
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);
Upvotes: 2
Reputation: 198
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));
Upvotes: 0
Reputation: 23955
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.)
Upvotes: 1