2014-07-30 2 views
1

У меня есть куча песен, хранящихся в массиве объектов JavaScript. Это выглядит так:Сортировка массива объектов JSON по свойствам с порядком важности

var library = [ 
{ "title": "40 Years Later",  "album": "Tears of Steel", "year": 2012, "track": 1, "disk": 1, "time": 31 }, 
{ "title": "The Dome",    "album": "Tears of Steel", "year": 2012, "track": 2, "disk": 1, "time": 311 }, 
{ "title": "The Battle",   "album": "Tears of Steel", "year": 2012, "track": 3, "disk": 1, "time": 123 }, 
{ "title": "End Credits",   "album": "Tears of Steel", "year": 2012, "track": 4, "disk": 1, "time": 103 }, 
{ "title": "The Wires",   "album": "Elephants Dream", "year": 2006, "track": 1, "disk": 1, "time": 75 }, 
{ "title": "Typewriter Dance", "album": "Elephants Dream", "year": 2006, "track": 2, "disk": 1, "time": 70 }, 
{ "title": "The Safest Place", "album": "Elephants Dream", "year": 2006, "track": 3, "disk": 1, "time": 45 }, 
{ "title": "Emo Creates",   "album": "Elephants Dream", "year": 2006, "track": 4, "disk": 1, "time": 60 }, 
{ "title": "End Title",   "album": "Elephants Dream", "year": 2006, "track": 5, "disk": 1, "time": 91 }, 
{ "title": "Teaser Music",  "album": "Elephants Dream", "year": 2006, "track": 6, "disk": 1, "time": 75 }, 
{ "title": "Ambience",   "album": "Elephants Dream", "year": 2006, "track": 7, "disk": 1, "time": 110 }, 
{ "title": "Snow Fight",     "album": "Sintel", "year": 2010, "track": 1, "disk": 1, "time": 107 }, 
{ "title": "Finding Scales/Chicken Run", "album": "Sintel", "year": 2010, "track": 2, "disk": 1, "time": 107 }, 
{ "title": "The Ziggurat",     "album": "Sintel", "year": 2010, "track": 3, "disk": 1, "time": 78 }, 
{ "title": "Expedition",     "album": "Sintel", "year": 2010, "track": 4, "disk": 1, "time": 93 }, 
{ "title": "Dragon Blood Tree",   "album": "Sintel", "year": 2010, "track": 5, "disk": 1, "time": 47 }, 
{ "title": "Cave Fight/Lament",   "album": "Sintel", "year": 2010, "track": 6, "disk": 1, "time": 145 }, 
{ "title": "I Move On (Sintel's Song)", "album": "Sintel", "year": 2010, "track": 7, "disk": 1, "time": 169 }, 
{ "title": "Circling Dragons",    "album": "Sintel", "year": 2010, "track": 8, "disk": 1, "time": 28 }, 
{ "title": "Trailer Music",    "album": "Sintel", "year": 2010, "track": 9, "disk": 1, "time": 44 } 
]; 

Мне нужно отсортировать по свойствам в алфавитном порядке и в цифровом порядке. В StackOverflow есть много статей и вопросов, которые охватывают сортировку по одному значению, а некоторые, похоже, охватывают несколько значений, но у них нет порядка или важности.

Мне нужно отсортировать каждый объект (песню) несколькими значениями, где каждое значение имеет более низкий рейтинг. Например:

album имя (алфавитный)>disk номер (числовой)>track номер (числовой)>title(алфавитный)> т.д.

Это означает, эти альбомы вместе, где каждый альбом находится в алфавитном порядке. В каждом альбоме песни сортируются по номеру диска, поэтому все песни на диске 1 находятся наверху, затем все песни на диске 2 и т. Д. В каждой группе номеров дисков записаны песни, отсортированные по номеру дорожки. Если есть несколько песен с одним и тем же номером трека или номер трека отсутствует, они будут отсортированы по названию песни в алфавитном порядке. Дополнительные свойства могут быть заданы, если название дорожки также одинаково.

Капитализация не должна иметь значения, и она должна выглядеть как обычная сортировка в алфавитном порядке (если она начинается с числа, которое будет идти до букв, а специальные символы будут иметь место, как в традиционной сортировке).

Я пробовал использовать этот код (который имеет жестко заданные значения сортировки и недостатки номеров дисков), но это только сортировка по самому внутреннему свойству в коде, номер дорожки.

library.sort(function (a, b) { 
    if (a.album === b.album) { 
     if (a.track === b.track) { 
      var x = a.title.toLowerCase(); 
      var y = b.title.toLowerCase(); 
      return x < y ? -1 : x > y ? 1 : 0; 
     } 
     var x = a.track; 
     var y = b.track; 
     return x < y ? -1 : x > y ? 1 : 0; 
    } 
    return a.album.toLowerCase() - b.album.toLowerCase(); 
}); 

Я ищу функцию, которая будет переставить library массив таким образом, чтобы он сортируется, как описано выше заданной что-то вдоль линий следующего ввода:

sort(library, [ "album", "disk", "track", "title" ]); 

Отдельные значения также должны быть в состоянии для сортировки в порядке убывания, возможно, выглядя что-то вроде -album или ["album", true]. Синтаксис является гибким.

ответ

2

Вы можете использовать функцию следующим образом:

function priority(opt) { 
    if (!(opt instanceof Array)) { 
     opt = Array.prototype.slice.call(arguments); 
    } 
    return function (a, b) { 
     for (var i = 0; i < opt.length; ++i) { 
      var option = opt[i]; 
      if (typeof option === 'string') { 
       option = [option, '+']; 
      } 
      if (option.length < 2) { 
       option[1] = '+'; 
      } 
      if (a[option[0]] !== b[option[0]]) { 
       if (a[option[0]] === undefined) return 1; 
       if (b[option[0]] === undefined) return -1; 
       if (typeof a[option[0]] === 'string' || typeof b[option[0]] === 'string') { 
        return (option[1] === '+' ? String(a[option[0]]).toLowerCase() < String(b[option[0]]).toLowerCase() : String(a[option[0]]).toLowerCase() > String(b[option[0]]).toLowerCase()) ? -1 : 1; 
       } else { 
        return (option[1] === '+' ? a[option[0]] < b[option[0]] : a[option[0]] > b[option[0]]) ? -1 : 1; 
       } 
      } 
     } 
     return 0; 
    }; 
} 

это работает так:

library.sort(priority(['album', 'disk', ['title', '-']]) 

это будет сортировать библиотеку альбома по возрастанию, затем диск возрастания, затем название по убыванию

Формальное использование:

opt: массив, содержащий:

  • строка (сортирует ключ 'строка' в порядке возрастания)

или

  • массив
    • 0: строка (сортирует ключ 'строка')
    • 1: '-' или '+' для восходящего или нисходящего (по умолчанию возрастает)

Я не реализовал его точно так, как вы сказали, потому что он будет делать ключи, начинающиеся с - невозможным для сортировки в порядке возрастания.

EDIT:

альтернативная версия, работает с '-album' синтаксисом:

function priority(opt) { 
    if (!(opt instanceof Array)) { 
     opt = Array.prototype.slice.call(arguments); 
    } 
    return function (a, b) { 
     for (var i = 0; i < opt.length; ++i) { 
      var order = opt[i].substr(0, 1), 
       key = opt[i].substr(1); 
      if (order !== '-' && order !== '+') { 
       key = opt[i]; 
       order = '+'; 
      } 
      if (a[key] !== b[key]) { 
       if (a[key] === undefined) return 1; 
       if (b[key] === undefined) return -1; 
       if (typeof a[key] === 'string' || typeof b[key] === 'string') { 
        return (order === '+' ? String(a[key]).toLowerCase() < String(b[key]).toLowerCase() : String(a[key]).toLowerCase() > String(b[key]).toLowerCase()) ? -1 : 1; 
       } else { 
        return (order === '+' ? a[key] < b[key] : a[key] > b[key]) ? -1 : 1; 
       } 
      } 
     } 
     return 0; 
    }; 
} 

использоваться как это:

library.sort(priority(['album', '-year', '+title'])); 
or 
library.sort(priority('album', '-year', '+title')); 
+0

забыли '<' в тройном выражении – Ferdi265

+0

В вашей альтернативной версии синтаксиса, является '+' опциональной? – Keavon

+0

Да, '+' не является обязательным. Это необходимо для того, чтобы можно было иметь ключи, начинающиеся с '-' (просто используйте' '+ - keywithdash'', чтобы сортировать такой ключ в порядке возрастания и' '--- keywithdash'' для нисходящего) – Ferdi265

1

Здесь вы идете .. один подход к проблеме:

// This is kinda hacky, but it works 
// "a" means do an alphabetical compare 
// "n" means do a numeric compare. 
var sortKeys = ["album", "a", "disk", "n", "track", "n", "title", "n"]; 

function byKey(ao, bo) { 
    var l = sortKeys.length; 
    var i = 0; 
    var sortResult; 

    // Walk through the keys 
    while (i < l) { 
     // Get the field name 
     var field = sortKeys[i]; 
     // Get the compare type 
     var sortType = sortKeys[i + 1]; 

     // Get the values and force to string values 
     var a = "" + ao[field]; 
     var b = "" + bo[field]; 

     console.log([field, sortType]); 
     // Advance by two because we consume two array elements 
     i += 2; 

     // Our alphabletical compare 
     if (sortType === "a") { 
      if (a.toLowerCase() < b.toLowerCase()) { 
       return -1; 
      } 

      if (a.toLowerCase() > b.toLowerCase()) { 
       return +1; 
      } 

      if (a.toLowerCase() === b.toLowerCase()) { 
       // Ok, these fields match. Restart the loop 
       // So it will try the next sort criteria in. 
       continue; 
      } 

      throw ("Should never actually get here."); 
     } 

     if (sortType === "n") { 
      // Cheap numeric compare 
      return +a - +b; 
     } 


    } 

    // A total match across all fields 
    return 0; 

} 

library.sort(byKey); 

console.log(JSON.stringify(library, null, 2)); 
+0

Можно ли преобразовать числа в строки и разрешить их сортировку одинаково ? Если бы альбом начинался с числа, это могло бы вызвать проблемы. – Keavon

+0

Он обрабатывает это сейчас ... хотя до тех пор, пока имя альбома было в кавычках, это будет строка, независимо от содержимого. Тем не менее, я сделал небольшое изменение, чтобы строгое число было правильно преобразовано в строку. Аналогично, все, что помечено для сортировки в виде числа, вынуждается числом. –

+0

Следует также отметить, что капитализация не должна иметь значения. Я не уверен, что это происходит в вашем решении. – Keavon

3

Вы можете отсортировать массив объектов с помощью Alasql JavaScript SQL. Он поддерживает сортировку со многими полями в любом порядке (например, SQL).

Для многих сортов Alasql может быть быстрее других функций сортировки, поскольку он компилирует запросы к JavaScript и сохраняет эту скомпилированную функцию запроса в кеш.

var res = alasql('SELECT *, LCASE(album) AS lalbum, LCASE(title) as ltitle FROM ? \ 
    ORDER BY lalbum DESC, disk, ltrack, ltitle',[library]); 

Здесь я создал два дополнительных поля (lalbum и ltitle) для сортировки текста в регистронезависимом образом.

Попробуйте этот пример с вами данными at jsFiddle

Смежные вопросы