After sorting, empty strings are at the beginning of the sorted array
vladshcherbin opened this issue · 6 comments
const test = [
{ title: 'b' },
{ title: '' },
{ title: 'a' }
]
console.log(arraySort(test, 'title'))
Output:
[
{ title: '' },
{ title: 'a' },
{ title: 'b' }
]
Expected output:
[
{ title: 'a' },
{ title: 'b' },
{ title: '' }
]
Since it's sorting by string, I'd expect empty strings to be last in the array, similar to null
values.
The sorting logic follows what would happen if you sort an array with just the values:
var arr = ['', null, undefined, 'a', 'c', 'b'];
console.log(arr.sort());
//=> ['', 'a', 'b', 'c', null, undefined]
I think this makes sense since an empty string can be compared to another string, but null
and undefined
always return false
:
console.log('' > 'a');
//=> false
console.log('' < 'a');
//=> true
console.log(null > 'a');
//=> false
console.log(null < 'a');
//=> false
console.log(undefined > 'a');
//=> false
console.log(undefined < 'a');
//=> false
I think if you need different functionality, then you can pass in a custom compare function
@doowb in your example, I'd expect result to be:
var arr = ['', null, undefined, 'a', 'c', 'b'];
//=> ['a', 'b', 'c', '', null, undefined]
Here is a more real-world example: you have movies and you want to sort them alphabetically by movie director. If a movie doesn't have a specified director, it will be at the beginning of the sorted array.
I'd expect to see movies with specified director first, starting from A to Z and movies with no specified director at the end.
const movies = [
{ title: 'Movie 1', director: 'Bill' },
{ title: 'Movie 2', director: '' },
{ title: 'Movie 3', director: 'Alex' }
]
arraySort(movies, 'director')
// current
[
{ title: 'Movie 2', director: '' },
{ title: 'Movie 3', director: 'Alex' },
{ title: 'Movie 1', director: 'Bill' }
]
// expected
[
{ title: 'Movie 3', director: 'Alex' },
{ title: 'Movie 1', director: 'Bill' },
{ title: 'Movie 2', director: '' }
]
My example was showing how the native JavaScript sort works and that ''
will sort to the beginning.
In your example, director
is undefined
so it will sort to the end as you expect.
I also did some tests in excel to see how an empty string is sorted and it will go to the beginning for ascending and end for descending.
We refactored the default comparison logic (that handles null
and undefined
) into default-compare so you can use it with a custom function to do the sorting that you're expecting:
var defaultCompare = require('default-compare');
function compare(prop) {
return function(a, b) {
var aVal = a && a[prop];
var bVal = b && b[prop];
if (aVal === '' && bVal !== '') {
return 1;
} else if (aVal !== '' && bVal === '') {
return -1;
}
return defaultCompare(a, b, prop);
};
}
var arr = [
{ title: 'Movie 1', director: 'Bill' },
{ title: 'Movie 2', director: '' },
{ title: 'Movie 3', director: 'Alex' }
];
console.log(arraySort(arr, compare('director')));
@doowb yeah, I can even provide a custom function to array.sort()
and don't use any libraries. :)
The whole point of using a library to sort arrays is convenience. If you think, that default sort is working correctly and empty strings should be first in array, then I guess I just need to find another library where empty strings are at the end of sorted array.
I missed director
field in my example, fixed it some minutes later.
As an idea, there could be options for such cases:
arraySort(array, comparisonArgs, options?);
// movies
arraySort(movies, 'director', { emptyStringsLast: true })
If you think, that default sort is working correctly and empty strings should be first in array, then I guess I just need to find another library where empty strings are at the end of sorted array.
That's fine, that's why this is open source :) I based my decision on how native JavaScript sorts arrays, how excel is doing it, and how I've seen other libraries sort.
there could be options for such cases:
If you'd like to do a PR for discussion, we'll take a look at it.