linq icon indicating copy to clipboard operation
linq copied to clipboard

orderBy not working with null values

Open jordanbrowneb opened this issue 4 years ago • 7 comments

We're getting some really weird behavior when using orderBy on arrays with null in them.

Below is a test case running linq 3.2.1 on node 10.15.3.

const Enumerable = require('linq');

const data = ['2019-10-01', null, '2019-09-12', '2019-09-15', null];
const result = Enumerable.from(data).orderBy(x => x).toArray();

result.forEach(x => console.log(x));

Expected output:

null
null
2019-09-12
2019-09-15
2019-10-01

Actual output:

2019-10-01
null
2019-09-12
2019-09-15
null

Thanks in advance for any help you can provide.

jordanbrowneb avatar Oct 16 '19 21:10 jordanbrowneb

This is the function used for comparison:

compare: function (a, b) {
            return (a === b) ? 0
                 : (a > b) ? 1
                 : -1;
        }

Values are reversed if (a>b). Unfortunately comparing something with null always returns false.

console.log('2019-09-12' > null)
false

console.log('2019-09-12' < null)
false

One solution would be to pass a better lambda to orderBy, causing the value to be sanitized before comparing:

const result = Enumerable.from(data).orderBy(x => (x===null) ? "" : x).toArray();

mihaifm avatar Oct 17 '19 19:10 mihaifm

Thank you for your quick reply. Do you intend to change the library to better handle nulls like in this case? Or is the workaround you posted going to be the permanent solution?

jordanbrowneb avatar Oct 22 '19 15:10 jordanbrowneb

Not sure yet... array.sort() does indeed sort the above values but it does so by converting nulls to strings.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

We can probably do the same for this library using some typeof wizzardry, but it feels like a very specific scenario.

mihaifm avatar Oct 22 '19 16:10 mihaifm

Gotcha. It's a fairly common use case for us ( e.g. calling .select(...) to grab a nullable property then sorting it using .orderBy(...) ).

We were surprised that the order returned was not the same as when doing the equivalent calls in C# LINQ. C# will return the expected output I listed earlier.

jordanbrowneb avatar Oct 22 '19 18:10 jordanbrowneb

This is the function used for comparison:

compare: function (a, b) {
            return (a === b) ? 0
                 : (a > b) ? 1
                 : -1;
        }

Values are reversed if (a>b). Unfortunately comparing something with null always returns false.

console.log('2019-09-12' > null)
false

console.log('2019-09-12' < null)
false

One solution would be to pass a better lambda to orderBy, causing the value to be sanitized before comparing:

const result = Enumerable.from(data).orderBy(x => (x===null) ? "" : x).toArray();

I suddenly realized that Intl could solve this problem image

zhongchengyi avatar Mar 10 '20 05:03 zhongchengyi

Now , you can set compare function , at 3.2.2 Issue: #74

const a= ['2019-10-01', null, '2019-09-12', '2019-09-15', null];
var cf = new Intl.Collator();
Enumerable.from(a).orderBy(x=>x, cf.compare).toArray()

output: ["2019-09-12", "2019-09-15", "2019-10-01", null, null]

You can prepare a custorm compare function to let null in front.

zhongchengyi avatar Mar 12 '20 03:03 zhongchengyi

warning:

You should have a reasonable initialization parameter,otherwise the result may not be what you want. see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator

Example:

var cf = new Intl.Collator();
cf.compare(1,2)
//=-1
cf.compare(15,2)
//=-1  (Expected = 1)

zhongchengyi avatar Mar 12 '20 10:03 zhongchengyi