0

I am sorting an array of objects and the sorting function seems to do not have any result on the array. So it returns the same list.

I exported the array and the sorting functions to the window and execute them manually then the resulting array is well sorted.

import { connect } from "react-redux"

import DestinationsGallery from "./destinationsGallery"

import DestinationFilter from "./functions/filter"
import WindPoint from "./functions/windPoint"
import {sortCosts,sortWind, sortName} from "./functions/sort"

const getSelectedDestinations = ( catalog, filter ) => { 
    let paramCatalog = null 
    let paramSorted = null

    if (catalog) {
        paramCatalog =  catalog
            .map(spot => WindPoint(spot,filter))
            .filter( item => DestinationFilter( item,filter ) )
            .sort( ( a,b ) => a.name.localeCompare( b.name ) )


        window.catalog = paramCatalog
        window.sortCosts = sortCosts
        window.sortWind = sortWind
        window.sortName = sortName

        console.log("Unsorted",paramCatalog)    
        paramSorted = paramCatalog.sort( sortName )
        console.log( "Sorted name",paraSorted, paramCatalog.sort( sortName ) )


        let paraSorted = paramCatalog.sort( sortWind )
        console.log( "Sorted wind",paraSorted )
    }
    return paramSorted
}

export default connect( store => ({
    destinations: getSelectedDestinations(store.kiteSpots.catalog.list, store.kiteSpots.filter),
    filter: store.kiteSpots.filter,
}),null)(DestinationsGallery)

and my sorting functions are:

export const sortCosts = ( a,b ) => ( a.data.CostsRank > b.data.CostsRank? 1 : -1 )  
export const sortWind = ( a,b ) => ( a.windPoint.probability > b.windPoint.probability ? 1 : -1 ) 
export const sortName = ( a,b ) => { return a.name > b.name ? 1 : -1 }

So I just would like the output list is correct

Muntram van Chen
  • 403
  • 1
  • 5
  • 10
  • btw, [Shouldn't returning a boolean be enough for a comparison function?](https://stackoverflow.com/questions/24080785/sorting-in-javascript-shouldnt-returning-a-boolean-be-enough-for-a-comparison). – Nina Scholz Nov 02 '19 at 14:44
  • @NinaScholz they're not returning a boolean – Patrick Roberts Nov 02 '19 at 14:45
  • @PatrickRoberts yes. but it omits one value - in this case zero for same values. so it applies here as well. (but this may not the solution of the problem.) – Nina Scholz Nov 02 '19 at 14:47
  • If they're equal it doesn't matter whether they're swapped or not. The problem with boolean being returned is that the function can never indicate a swap. – Patrick Roberts Nov 02 '19 at 14:47
  • 1
    Actually the function are working on the browser window, but I do not understand why in my code they do not produce a sorted result. – Muntram van Chen Nov 02 '19 at 14:54
  • @MuntramvanChen if you don't want to receive a flood of answers about the "correct" way to sort an array, I suggest you provide a [mcve]. If you're using ES module syntax, I would [edit] your question to indicate what code is in which file, and any error messages you receive. – Patrick Roberts Nov 02 '19 at 15:11
  • If `a.data.CostsRank` and `b.data.CostsRank` are equal, then `-1` is returned from the `compareFunction`. This will result in inconsistent results. You need to return zero when both are same. If it is a number field, use `a.data.CostsRank - b.data.CostsRank` For strings, use `a.name.localeCompare(b.name)` – adiga Nov 03 '19 at 06:54

2 Answers2

1

This code does not mutates (sort) the catalog array if it's what you expected, only param catalog will be sorted as the sort is applied to the result of the mapping and filtering of catalog which both return new array instances.

Maybe if you post more code and source data we could find what's wrong.

That said, you shouldn't compare strings this way, using a mathematical operator, as it may not behave as you expect.

This discussion from stackoverflow explains a bit the differences. In short codepoint order (used when comparing with >) is not the same as actual alphabetical order.

Also, the return value of a comparison function should be 1, -1 or 0 because not swaping equal values sometimes matter.

For instance sorting [0,0,1,1,2,2,3,3] by allowing swapping equal comparing value does not matter.

But sorting [{ name: 'foo', value: 0},{ name: 'bar', value: 0},{ name: 'fuu', value: 1},{ name: 'ber', value: 1}] according to value may change the order of the original array when it may not be desirable.

It's true that older implementation of the native sort did not care for equal comparing value and may have the order anyway but now nore implementations respect the contract of not modifying already sorted arrays.

If you care about alphabetical order of unicaode strings, try and use String.prototype.localeCompare.

If you're comparing numbers (say a and b), and if you don't have to deal with NaN or infinity or undefined, it is more correct and requires less native instructions to return a-b for ascending order and b-a for descending order.

remix23
  • 2,632
  • 2
  • 11
  • 21
  • `because not swaping equal values sometimes matter` if they're equal, it _wouldn't_ matter. – Patrick Roberts Nov 02 '19 at 14:55
  • When using a sorting callback you can sort say an array of objects and use one of the properties of the object as the actual sorting value. if you swap equal values according to this property you may for instance change the initial order of an already sorted array. So yes in some cases it does matter. It does not matter when equal sorting values strictly means equal values which is definitely not everytime true. – remix23 Nov 02 '19 at 14:58
  • If the property is the only sorting criteria then the whole objects are still considered equal by nature of comparing only the property. Besides, before ECMAScript 2020, the native `sort()` method didn't even guarantee stability _anyway_ so it didn't matter whether the callback was capable of returning `0`. – Patrick Roberts Nov 02 '19 at 15:00
  • Equal sorting criteria does not strictly mean equal value. And you can introduce undesired behavior if you don't account for it – remix23 Nov 02 '19 at 15:02
  • I ran into the unstability of the native sort server side, I resorted to a reimplementation of bubble sort in older Node :/ Still, swapping equal comparison could be hazardous and less efficient. – remix23 Nov 02 '19 at 15:06
  • "_**bubble sort**_... _swapping equal comparison could be hazardous and **less efficient**_" really? also if you care about efficiency, `localeCompare` is _dog slow_ compared to sorting by string inequality. – Patrick Roberts Nov 02 '19 at 15:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201763/discussion-between-remix23-and-patrick-roberts). – remix23 Nov 02 '19 at 15:08
1

You can try using String.prototype.localeCompare for comparing strings. See MDN.

const x = [
  { name: "James"},
  { name: "Zeb"},
  { name: "Annie"},
  { name: "Michaèl"},
  { name: "James"},
];

console.log( x.slice(0).sort( (a, b) => a.name.localeCompare(b.name) ) );
// include value equality
console.log( x.slice(0).sort( (a, b) => -(a.name < b.name) || +(a.name > b.name)) );

Or include the possible value equality in comparing:

a.name > b.name ? 1 : a.name < b.name ? -1 : 0) or (see comments)

-(a.name < b.name) || +(a.name > b.name)

Or (for performance) you can use Intl.Collator (see MDN and the next snippet)

const intlCollator = new Intl.Collator("de", {sensitivity: "variant"});
console.log( 
  [
    { name: "Anna"},
    { name: "Michiel"},
    { name: "Desiré"},
    { name: "Siënna"},
    { name: "Jøn"},
    { name: "Mariêke"},
    { name: "José"},
    { name: "Anna"},
    { name: "Renée"},
    { name: "Désirée"},
  ].sort( (a, b) => 
      intlCollator.compare(a.name, b.name) )
);
.as-console-wrapper { top: 0; max-height: 100% !important; }
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • `(a, b) => a.name > b.name ? 1 : -1` works just as well for this particular example. The only place `localeCompare` would improve the ordering is if the `è` would need to be compared to an unaccented letter `f` through `z` – Patrick Roberts Nov 02 '19 at 14:52
  • @PatrickRoberts sure, included it in the answer. `localeCompare` is a bit shorter, may also be used for comparing accents/diacriticals etc. and also returns 1, -1, or 0. – KooiInc Nov 02 '19 at 15:02
  • If you're going to include the inequality approach, might I suggest `(a, b) => -(a.name < b.name) || +(a.name > b.name)`? Also, `localeCompare` is _dog slow_, so that might also be a consideration. – Patrick Roberts Nov 02 '19 at 15:04