2013-07-10 12 views
7

Pracuję z platformą e-commerce, która nie ma możliwości zmiany kolejności opcji w polach atrybutów produktu. To naprawdę jest do bani, ponieważ aby wstawić nową opcję, musisz usunąć wszystkie istniejące i zacząć od nowa. Próbuję zrobić to po stronie klienta. Oto, co pracuję z (ten dotyczący rozmiar buta):Opis sortowania() compareFunction

  • 9 EE
  • 9 1/2 EE
  • 10 EE
  • 10 1/2 EE
  • 11 EE
  • 11 1/2 EE
  • 9 EEEE
  • 9 1/2 D
  • 9 1/2 EEEE
  • 10 EEEE
  • 10 1/2 EEEE
  • 11 EEEE
  • 9d
  • 11 1/2 EEEE

Te są rzeczywiście tekst niektórych <option> S w formie. Format wartości jest X Y Z gdzie:

  • X jest liczbą całkowitą
  • Y jest napis „1/2” i nie może być obecny
  • Z kod jest list, który jest albo „D ”, "E", "SEE" lub "EEEE" i nie może być obecny

żądanej kolejności powyższe będzie to:

  • 9d
  • 9 1/2 D
  • 9 EE
  • 9 1/2 EE
  • 9 EEEE
  • 9 1/2 EEEE
  • 10 EE
  • 10 1/2 EE
  • 10 EEEE
  • 10 1/2 EEEE
  • 11 EE
  • 11 1/2 EE
  • 11 EEEE
  • 11 1/2 EEEE

nauczyłem się trochę o sort() funkcji JavaScript, ale nie były w stanie w pełni zrozumieć, jak porównanie funkcja, którą możesz przekazać działa.Mam to do tej pory:

<select> 
    <option>9 EE</option> 
    <option>9 1/2 EE</option> 
    <option>10 EE</option> 
    <option>10 1/2 EE</option> 
    <option>11 EE</option> 
    <option>11 1/2 EE</option> 
    <option>9 EEEE</option> 
    <option>9 1/2 D</option> 
    <option>9 1/2 EEEE</option> 
    <option>10 EEEE</option> 
    <option>10 1/2 EEEE</option> 
    <option>11 EEEE</option> 
    <option>9 D</option> 
    <option>11 1/2 EEEE</option> 
</select> 

Zacząłem kodu pobranego z tej odpowiedzi: https://stackoverflow.com/a/667198/398242

$("select").html($("option").sort(function (a, b) { 
    return a.text == b.text ? 0 : a.text < b.text ? -1 : 1 
})); 

Jakie sortuje rzeczy tak (nie działa nawet pierwszy kryteria):

  • 10 1/2 EE
  • 10 1/2 eeee
  • 10 EE
  • 10 EEEE
  • 11 1/2 EE
  • 11 1/2 EEEE
  • 11 EE
  • 11 EEEE
  • 9 1/2 D
  • 9 1/2 EE
  • 9 1/2 EEEE
  • 9d
  • 9 EE
  • 9 EEEE

widzę, że w javascript '11' > '9' powraca false, która w żaden sposób nie czyni dla mnie sens.

MDN describes the compare function argument jako taki, a ja niby dostać go:

function compare(a, b) { 
    if (a is less than b by some ordering criterion) 
    return -1; 
    if (a is greater than b by the ordering criterion) 
    return 1; 
    // a must be equal to b 
    return 0; 
} 

... ale nie mam pojęcia jak do tego dostosować, aby dopasować moje wymagania. Próbowałem kilku rzeczy, ale po prostu mam wrażenie, że strzelam w ciemności. Próbowałem pokazać, że poświęciłem trochę czasu, aby spróbować zrozumieć ten problem. Chciałbym dowiedzieć się więcej, ale na razie chciałbym rozwiązać ten problem.

http://jsfiddle.net/DnwJ6/ Jakieś wskazówki?

+0

Myślę, że tutaj rozwiązaniem jest jakoś normalizacja zarówno "a" jak i "b" do formatu '\ d \ d (1/2 | 0/0) [az] *' przed porównaniem ich –

+0

'" 11 ">" 9 "=== false' ma doskonały sens. Porównujesz ** ciągi **. Spróbuj przekonwertować najpierw na numery: – Ian

+0

Checkout http://jsfiddle.net/arunpjohny/ZnLJh/ –

Odpowiedz

1

Sprawdź to:

$("select").html($("option").sort(function (a, b) { 
    var regex = /(\d+)((?: 1\/2)?)([DE]+)/; 
    var abreakdown = a.text.match(regex), bbreakdown = b.text.match(regex); 
    if (parseInt(abreakdown[1]) === parseInt(bbreakdown[1])) { 
     if (abreakdown[3] === bbreakdown[3]) { 
      return (abreakdown[2] === bbreakdown[2] ? 0 : (abreakdown[2] < bbreakdown[2] ? -1 : 1));  
     } else { 
      return abreakdown[3] < bbreakdown[3] ? -1 : 1; 
     } 
    } else { return parseInt(abreakdown[1]) - parseInt(bbreakdown[1]); } 
})); 

Używa wyrażenia regularnego, aby podzielić elementy, a następnie wykonać porównanie na podstawie każdego komponentu.

Demo fiddle.

+0

Slam dunk, idealnie dopasowuje oczekiwany wynik, dzięki., –

+1

Świetne, mam nadzieję, że było pomocne. Btw, ''11 '>' 9 ''zwraca' fałsz', ponieważ jest to porównanie łańcuchowe.} Widzisz, użyłem 'parseInt()' na łańcuchach przed porównaniem ich do użycia ich wartości liczbowych zamiast. – acdcjunior

+0

Kilka problemów, gdy łańcuch nie pasuje w ogóle wzór (nieokreślone błędy indeksu), więc będę musiał podłączyć jakąś obronę, zanim będę mógł użyć tego na całym świecie.) Testuję jutro, jestem wyczerpany - znowu raz ... –

2

Put wartości w tablicy w kolejności chcesz je:

var shoeSizes = [ '9 D','9 1/2 D','9 EE','9 1/2 EE', ...] 

Używaj Array.protoype.indexOf (with a shim for older browsers), aby uzyskać indeks dopasowanego tekstu w tablicy. Użyj indeks dla wartości porównać, coś jak:

function(a,b) { 
    return shoeSizes.indexOf(a) - shoeSizes.indexOf(b); 
} 

Jeśli potrzebujesz do czynienia z wartościami, które nie są w tablicy, testować wartość zwracaną przez indexOf i zastąpić domyślną jeśli to -1 .

Alternatywnie można dokonać rozmiarów nazwy wartości nieruchomości w obiekcie i przypisać konkretną wartość:

var shoeSizes = { '9 D': 5, '9 1/2 D': 10, '9 EE': 15, '9 1/2 EE': 20, ...}; 

następnie użyj wartości w porównania:

function(a,b) { 
    return shoeSizes[a] - shoeSizes[b]; 
} 

bądź umożliwienia wartości domyślne:

function(a,b) { 
    return (a in shoeSizes? shoeSizes[a] : 1000) - (b in shoeSizes? shoeSizes[b] : 1000); 
} 
+0

Pracuję z istniejącym HTML, więc będę musiał to trochę przeżuć, zanim będę mógł je zastosować, dzięki za odpowiedź. –

+0

Zdecydowanie nie mogę użyć żadnej z tych opcji. Klient podaje wartości, po prostu chcę ślepo patrzeć na ciągi. Nie jestem pewien, w jaki sposób * "Umieść wartości w tablicy w kolejności, w jakiej chcesz" * jest przydatna tutaj, gdybym mógł to zrobić, mój problem byłby już rozwiązany. Nie wiem, jakie są wartości, tylko format. W każdym razie dzięki. –

+0

Jeśli nie wiesz, jakie są wartości, skąd wiesz, jak je sortować? Wydaje się, że chcesz uporządkować pewne rzeczy, nie wiedząc, jak je wcześniej posortować. – RobG

2

Ponieważ masz sformatowany tekst, jednym ze sposobów jest normalizacja tekstu elementy przed ich porównywaniem.

To rozwiązanie nie może być tak, że optymalna, ale będzie wykonać zadanie

$("select").html($("option").sort(function (a, b) { 
    return nomalize(a.text) < nomalize(b.text) ? -1 : 1; 
})); 

function nomalize(val){ 
    var parts = val.split(' '), op = ''; 

    op = parts[0].length == 1 ? '0' + parts[0] : parts[0]; 
    if(parts.length > 1){ 
     if(/[a-z]/i.test(parts[1])){ 
      op += '0/0' + parts[1]; 
     } else { 
      op += parts[1] 
     } 
    } 

    op += parts.length > 2 ? parts[2] : ''; 
    return op; 
} 

Demo: Fiddle

Jeśli ktoś może zaproponować jakieś rozwiązanie, aby zoptymalizować go dalej to będzie wielkie

+0

To nie pasuje do pożądanego wyjścia (patrz rozmiary 1/2), ale na pewno to zrobi na razie. Będę musiał pomyśleć o tym później, abym mógł to zrozumieć, bo teraz potrzebuję trochę kodu, żeby zatrzasnąć się na stronie, abym mogła pójść spać. Dzięki. –

+0

@WesleyMurch trochę lepiej można znaleźć na http://jsfiddle.net/arunpjohny/ZnLJh/4/ –

+0

@WesleyMurch checkout http://jsfiddle.net/arunpjohny/ZnLJh/5/ –

1

Pierwszą rzeczą, którą musisz zrozumieć, jest to, że 12 jest w rzeczywistości mniej niż 9, jeśli sortujesz nie jako liczby. To dlatego, że <1><2> jest mniejszy niż <9><nothing>, ponieważ 1 i i są w tym przypadku "kluczami głównymi".

Drugi problem, który napotykasz, polega na tym, czy długość (9 1/2) jest kluczem podstawowym, czy szerokość (EE). Podejrzewam, że ten pierwszy będzie miał więcej sensu, więc kontynuuj na tej podstawie.

Po podjęciu decyzji najlepiej jest podać funkcję sortowania połączenia, która zamienia każdy ciąg znaków na wartość liczbową, a następnie porównuje tę wartość. Na przykład:

  1. Zdobądź pierwsze pole (rozdzielone spacjami) (9) i ustaw dla niego wartość.
  2. Jeśli następne pole istnieje i jest 1/2, dodaj 0.5 do tej wartości.
  3. Jeśli ostatnia dziedzina istnieje (alfa jeden), po prostu przekonwertować do jakiejś wartości mniejszej 0.5 i dodać go (np a -> 0,01, B -> 0.02, ..., EEEE -> 0,08 i tak dalej).

To ostatnie zależy od względnej kolejności szerokości, wybrałem typowy amerykański system.

W rezultacie otrzymujemy wartość, która dyktuje prawidłową kolejność, a funkcja sortowania może po prostu dokonać porównania numerycznego. Przykładem następująco:

function xlat(s) { 
    var s2 = s.split(" "); 

    var n = parseInt(s2[0]); 
    if (s2.length == 1) { return n; } 

    var last = s2[1]; 
    if (last == '1/2') { 
     n = n + 0.5; 
     if (s2.length == 2) { return n; } 
     last = s2[2]; 
    } 
    var widths = ['A','B','C','D','E','EE','EEE','EEEE','F','G']; 
    n = n + widths.indexOf(last)/100; 
    return n; 
} 

$("select").html($("option").sort(function (a, b) { 
    var na = xlat(a.text); 
    var nb = xlat(b.text); 
    return na == nb ? 0 : na < nb ? -1 : 1; 
})); 

W xlat funkcji jest ważne tutaj. Najpierw dzieli rozmiar na tablicę 1, 2 lub 3 elementów i pobiera wartość numeryczną dla pierwszego. Jeśli nie ma drugiego i trzeciego numeru, ta wartość zostanie zwrócona (obsługuje "nagie" rozmiary, takie jak 9 lub 13).

W przeciwnym razie decyduje, czy jest to długość połowy inkrementu - decyduje, czy drugie pole to 1/2. W tym momencie wykrywa również, czy nie ma szerokości i zwraca rozmiar.

Po przekroczeniu tego punktu mamy rozmiar (cały lub pół), a zmienna last zachowuje szerokość. Następnie dodajemy wartość w oparciu o pozycję tego rozmiaru w tablicy, odpowiednio zmodyfikowaną (podzieloną przez 100), aby nie wpłynęła na klucz główny.

Za pomocą tego kodu z własną rękę, można dostać się (zgodnie z oczekiwaniami):

9 D 
9 EE 
9 EEEE 
9 1/2 D 
9 1/2 EE 
9 1/2 EEEE 
10 EE 
10 EEEE 
10 1/2 EE 
10 1/2 EEEE 
11 EE 
11 EEEE 
11 1/2 EE 
11 1/2 EEEE 
1

Najpierw należy zdefiniować odpowiedni klucz sortowania za pomocą którego można dokonać rozsądnych porównań; następująca funkcja używa wyrażenia regularnego do wyszukania użytecznych bitów informacji:

function sortkey(val) 
{ 
    var matches = val.match(/^(\d+)(1\/2)? (\w+)$/), 
    number = +matches[1]; 

    if (matches[2]) { 
     number += 0.5; // add "1/2" 
    } 

    return [number, matches[3]]; 
} 

Pierwszy mecz jest rzutowany na liczbę; jeśli drugi mecz jest dostępny, dodano 0.5. Następnie ostatni mecz zostanie dodany jako klucz sortowania wtórnego. Wartością zwracaną jest coś takiego:

[9.5, 'EE'] 

Struktura ta może być następnie wykorzystana do funkcji porównania:

function compareFunc(a, b) { 
    var sa = sortkey(a.text), 
    sb = sortkey(b.text); 

    if (sa[0] == sb[0]) { 
     return sa[1] < sb[1] ? -1 : 1; 
    } else { 
     return sa[0] < sb[0] ? -1 : 1; 
    } 
} 

zastosowane do konkretnego kodu:

var $sorted = $('select > option').sort(compareFunc); 
$('select').html($sorted); 

Demo

+0

Rozrzutki dookoła, nie będę w stanie naprawdę pozwolić, aby wszystkie te informacje zatonęły w celu długoterminowego zrozumienia dzisiejszej nocy, będą musiały przeczytać jutro. W tej chwili, przyjmując odpowiedź, która karmiła mnie łyżeczką, dokładnie to, czego potrzebowałem. –

+0

@WesleyMurch Wystarczająco fair; Zaletą tego podejścia jest to, że logika budowania klucza sortowania jest oddzielona od faktycznego procesu sortowania, dzięki czemu obie części są łatwiejsze do dostosowania :) –

+0

@WesleyMurch Po ponownym przeczytaniu kodu zdałem sobie sprawę, że popełniłem błąd; porównania tablic wymuszają porównanie oparte na łańcuchach :(zaktualizowano kod, aby nadal korzystać z funkcji generatora kluczy sortujących: –

1

widzę masz już roztwór roboczy, ale tylko dla porównania (gra słów nie przeznaczonych), tutaj jest jednym z wielu innych sposobów, aby to zrobić (fiddle):

// Make a shoe size sortable as text 
function sortableSize(text) { 
    // Split the size into parts separated by spaces 
    var parts = text.split(' '); 
    // The first part is the size number; 
    // make sure it is two digits (09,10,etc.) 
    if(parts[0].length == 1) 
     parts[0] = '0' + parts[0]; 
    // If it wasn't a 1/2 size, make it 0/2 
    if(parts.length == 2) 
     parts.splice(1, 0, '0/2'); 
    // So '9 EE' becomes '09 0/2 EE' 
    return parts.join(' '); 
} 

var $options = $('#sizes option'); 
$options = $options.sort(function(a, b) { 
    a = sortableSize(a.text); 
    b = sortableSize(b.text); 
    return a < b ? -1 : a > b ? 1 : 0; 
}); 
$('#sizes').html($options); 

ten Metoda tworzy tekstową reprezentację każdego rozmiaru obuwia, który można bezpośrednio sortować jako tekst. Następnie sortuje za pomocą tych reprezentacji tekstowych.

+0

Dzięki, myślę, że mam teraz jak to zrobić w ogóle. Okazuje się, że muszę wykonać więcej pracy/testów, aby móc działać globalnie w każdym produkcie w sklepie, więc może i tak zmienię akceptowaną odpowiedź, po prostu nie chcę, aby ludzie też wydawali dużo czasu na mój dość wąski problem, podczas gdy ja miałem "wystarczająco dobrą" odpowiedź, ale odpowiedzi z pewnością pomogły mi zrozumieć na następny raz. –