# What is wrong in this sorting array skipping some elements?

Stack Overflow Asked by Eymerich on December 26, 2020

I have array of objects. I want sort it leave some elements in the same position (with b="NOT")

var a=[{a:1,b:"YES"},{a:2,b:"YES"},{a:5,b:"NOT"},{a:0,b:"NOT"},{a:0,b:"YES"}]

function sortc(x,y){

if (x.b=="NOT" || y.b=="NOT")
return Infinity ;

return (Number(x.a)-Number(y.a))

}

console.log(a.sort(sortc));

the result is :

0: {a: 1, b: "YES"}
1: {a: 2, b: "YES"}
2: {a: 5, b: "NOT"}
3: {a: 0, b: "NOT"}
4: {a: 0, b: "YES"}


The expected result was ( with sort components with b="YES".) :

{ "a": 0, "b": "YES" }
{ "a": 1, "b": "YES" }
{ "a": 5, "b": "NOT" }
{ "a": 0, "b": "NOT" }
{ "a": 2, "b": "YES" }


You cannot only sort some items using the Array#sort() method - you either sort all or none. You don't define the position of the items, either - you only have to define their relationship to other items and the sorting algorithm will take care of the rest.

What you can do as a workaround is

1. Extract all items that should be sorted.
2. Sort them.
3. Go over the original array and only replace anything that should be sorted, leave the rest of the items in their place.

var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]

//get only b: "YES" items
const dataToSort = a.filter(item => item.b === "YES");

//sort them
dataToSort.sort((x, y) => x.a - y.a);

//replace only items that need to be sorted
const it = dataToSort.values()
for (let i = 0; i < a.length; i++) {
if (a[i].b === "NOT")
continue;

a[i] = it.next().value;
}

console.log(a);

For the record, the final loop can just be replaced with even shorter with more iterator usage, although it might be slightly more confusing:

const it = dataToSort.values()
for (const [key, item] of a.entries()) { //use the key-value iterator from the array
if (item.b === "NOT")
continue;

[a[key]] = it; //array destructuring internally advances an iterator
}


var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]

//get only b: "YES" items
const dataToSort = a.filter(item => item.b === "YES");

//sort them
dataToSort.sort((x, y) => x.a - y.a);

//replace only items that need to be sorted
const it = dataToSort.values()
for (const [key, item] of a.entries()) {
if (item.b === "NOT")
continue;

[a[key]] = it;
}

console.log(a);

Finally, this can be made somewhat more convenient with helper generator function and few small utility functions

/*  library code  */

const transformArg = transform => f => (...args) => f(transform(...args));

function* filter(predicate, it) {
for (const item of it) {
if (predicate(item))
yield item;
}
}

/*  /library code  */

var a = [
{ a: 1, b: "YES" },
{ a: 2, b: "YES" },
{ a: 5, b: "NOT" },
{ a: 0, b: "NOT" },
{ a: 0, b: "YES" }
]

/* helpers */
//extract the b key in this case so we don't need to repeat it.
const getSortableAttribute = transformArg(({b}) => b);
//get the value from key-value pair
const getValue = transformArg(([, value]) => value);
//check if the attribute is "YES"
const isSortable = getSortableAttribute(attr => attr === "YES");

const dataToSort = a.filter(isSortable);

dataToSort.sort((x, y) => x.a - y.a);

const it = dataToSort.values()
//iterate only over sortable key-value pairs by re-using the isSortable filter
for (const [key, item] of filter(getValue(isSortable), a.entries())) {
[a[key]] = it;
}

console.log(a);

Correct answer by VLAZ on December 26, 2020

This is an approach by using sort directly, but shaping the access with a Proxy for length and the indices.

const
sortOnly = (array, indices) => new Proxy(array, {
get (target, prop) {
if (isFinite(prop)) return target[indices[prop]];
if (prop === 'length') return indices.length;
return target[prop];
},
return true;
}
}),
array = [{ a: 1, b: "YES" }, { a: 2, b: "YES" }, { a: 5, b: "NOT" }, { a: 0, b: "NOT" }, { a: 0, b: "YES" }];

sortOnly(array, [...array.keys()].filter(i => array[i].b !== 'NOT'))
.sort((a, b) => a.a - b.a)

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

Answered by Nina Scholz on December 26, 2020