JavaScript sortuje tablicę według dwóch pól

88
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

Zatem powyższy kod sortuje tablicę według gsize - od najmniejszej do największej. Działa dobrze. Ale jeśli gsize jest taki sam, chciałbym, żeby to sortował według blasku.

Dzięki.

znak
źródło
Funkcja sort reaguje na wynik dodatni, ujemny lub zerowy. więc możesz po prostu napisać: "return aSize - bSize". będzie to prostszy i bardziej czytelny kod.

Odpowiedzi:

107
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});
Chris Eberle
źródło
170
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

krótsza wersja

anmorozov23
źródło
świetny skrót! pomógł mi położyć bardziej kompleksowe rozwiązanie razem .. stackoverflow.com/questions/6101475/...
Joseph Poirier
3
Ładnie i czysto! Jedyne, co działa tylko dla liczb.
Afanasii Kurakin
Czy możesz wyjaśnić tę logikę?!. U mnie zadziałało, sort an array with a key's value firsta potemsort the result with another key's value
KTM
1
@KTM Logika jest następująca: jeśli oba gsize są równe, to pierwsza część warunku jest równa 0, co jest uważane za fałsz, a druga część warunku jest wykonywana.
Scalpweb
@Scalpweb Tak :) więc to działa, aby posortować tablicę z dowolną liczbą kluczy jeden po drugim, prawda ?! Niezła sztuczka
KTM
35
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);

Jeszcze krótsza wersja ze składnią strzałek!

Vinorth
źródło
3
Najbardziej zwięzły i rozszerzalny, doskonały!
Laurent
1
Jeszcze krócej, usuń spacje:grouperArray.sort((a,b)=>a.gsize-b.gsize||a.glow-b.glow);
Kapitan Fantastyczny
jeśli chcesz zrozumieć, dlaczego to działa, spójrz na medium.com/@safareli/pss-ordering-is-a-monoid-61a4029387e
Safareli
14

Zdaję sobie sprawę, że pytano o to jakiś czas temu, ale pomyślałem, że dodam swoje rozwiązanie.

Ta funkcja dynamicznie generuje metody sortowania. po prostu podaj każdą możliwą do sortowania nazwę właściwości podrzędnej, poprzedzoną znakiem +/-, aby wskazać kolejność rosnącą lub malejącą. Super do ponownego użycia i nie musi nic wiedzieć o utworzonej strukturze danych. Można to uczynić idiotą - ale nie wydaje się konieczne.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

przykład użycia:

items.sort (getSortMethod ('- cena', '+ priorytet', '+ nazwa'));

posortowałoby to itemsod najniższej do pricepierwszej, z powiązaniami prowadzącymi do pozycji o najwyższej priority. dalsze więzi są zrywane przez przedmiotname

gdzie elementy to tablica taka jak:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

demo na żywo: http://gregtaff.com/misc/multi_field_sort/

EDYCJA: Naprawiono problem z Chrome.

nihlton
źródło
To jest genialne
Azure
Genialna odpowiedź!
Marius
dla maszynopisu (aby nie uzyskać error TS2554: Expected 0 arguments, but got ..) użyj składni tutaj: stackoverflow.com/a/4116634/5287221
Chananel P
6

Spodziewam się, że operator trójskładnikowy((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;) cię pomylił. Powinieneś sprawdzić link, aby lepiej go zrozumieć.

Do tego czasu, oto twój kod wydmuchany w pełni if ​​/ else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});
John Green
źródło
6

Oto implementacja dla tych, którzy mogą chcieć czegoś bardziej ogólnego, który działałby z dowolną liczbą pól.

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

Zasadniczo możesz określić dowolną liczbę nazw właściwości / kierunku sortowania:

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe
The_Black_Smurf
źródło
3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});
silex
źródło
3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 
Tim Williams
źródło
3

Oto implementacja, która używa rekurencji do sortowania według dowolnej liczby pól sortowania od 1 do nieskończoności. Przekazujesz mu tablicę wyników, która jest tablicą obiektów wynikowych do sortowania, oraz tablicę sortów, która jest tablicą obiektów sortowania definiujących sortowanie. Każdy obiekt sortowania musi mieć klucz „select” dla nazwy klucza, według którego sortuje, oraz klucz „order”, który jest łańcuchem oznaczającym „rosnąco” lub „malejąco”.

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)
Benjamin Portman
źródło
2

Dynamiczny sposób na zrobienie tego za pomocą WIELU klawiszy:

  • filtruj unikalne wartości z każdego sortowania kolumny / klucza
  • uporządkować lub odwrócić
  • dodaj wagi szerokość zeropad dla każdego obiektu na podstawie wartości kluczy indexOf (value)
  • sortuj według obliczonych wag

wprowadź opis obrazu tutaj

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Posługiwać się:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

wprowadź opis obrazu tutaj

Leonardo Filipe
źródło
1

To jest to, czego używam

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

połącz oba elementy jako ciąg, a zostaną one posortowane według wartości ciągu. Jeśli chcesz, możesz zawinąć _a i _b parseInt, aby porównać je jako liczby, jeśli wiesz, że będą numeryczne.

blockloop
źródło
1

Oto rozwiązanie dla przypadku, gdy masz priorytetowy klucz sortowania, który może nie istnieć w niektórych konkretnych elementach, więc musisz sortować według kluczy rezerwowych.

Przykład danych wejściowych ( id2 to priorytet sortowania klucza):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

Wynik powinien być:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

Funkcja komparatora będzie wyglądać następująco:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

PS W przypadku, gdy .id z .id2 może być zerami, rozważ użycie typeof.

Deliaz
źródło
0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);
ic3b3rg
źródło
0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

nie testowane, ale myślę, że to powinno działać.

Xander
źródło
0

W moim przypadku sortuję listę powiadomień według parametru „ważne” i „daty”

  • krok 1: filtruję powiadomienia według „ważnych” i nieważnych

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • krok 2: sortuję je według daty

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • krok 3: scal je

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    
Phạm Hùng
źródło