Последовательность сортировки многомерного массива кросс-браузерно-совместима и с «натуральным регистром» - PullRequest
3 голосов
/ 30 апреля 2019

Мне нужно изощренно отсортировать несколько больших массивов (1000-2000 ключей) для моего веб-приложения.Я немного поработал в Safari 12.0 / FF 66.0, но chrome 74.0, похоже, делает что-то совсем другое.

Последовательность, которую я хочу отсортировать - без учета регистра, с естественным регистром:

1. "scene"  
2. "shot"  
3. "take"  
4. "name" 

Каждое из этих значений может быть строкой (например, 4, 4b или 4-PU üöä!") или 'undefined' и может выглядеть следующим образом:

[
  {"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
  {"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
  {"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
  {"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
  {"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
  {"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
  {"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
  {"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
  {"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
  {"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
  {"scene": "1-b", "shot": "1", "take": "3", "name": "A004C007_170718_R1W0*"},
  {"scene": "5", "shot": "5PU", "take": "6", "name": "A021C005_170716_R1W0*"},
]

Если клавиша scene имеетне существует, сортируйте до конца и сортируйте по «имени».

Это часть проекта vue.js, и я надеюсь сделать это без дополнительной библиотеки.

Вот моя функциядо сих пор.Я использую localCompare() для сравнения, например.4a до 4 или .Я уверен, что это очень медленно и может быть намного лучше!

sortedFiles = sortClips(files)

sortClips(clips) {
  let firstBy=(function(){function e(f){f.thenBy=t;return f}function t(y,x){x=this;return e(function(a,b){return x(a,b)||y(a,b)})}return e})();

  let options = {
    numeric: true,
    sensitivity: 'base',
    ignorePunctuation: true
  };

  clips.sort(
    firstBy(function (v1, v2) {
      if (!v1.scene) {
        return v1.name.localeCompare(v2.name, undefined, options);
      }
      return v1.scene.localeCompare(v2.scene, undefined, options);
    })
      .thenBy(function (v1, v2) {
        if (!v1.shot) return -1;
        return v1.shot.localeCompare(v2.shot, undefined, options);
      })
      .thenBy(function (v1, v2) {
        if (!v1.take) return -1;
        return v1.take.localeCompare(v2.take, undefined, options);
      })
  );
  return clips;
},

Исходные данные (сцена, снимок, дубль, имя):

1 1 4 A031C006_170718_R1W0
8 8 4 A020C004_170716_R1W0
1 1 10 A031C013_170718_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
2 2 1 A008C010_170715_R1W0
5 5 1 A015C005_170716_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
1 1 3 A004C006_170714_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*

Firefox / safari sorts:

8 8 4 A020C004_170716_R1W0 
1 1 4 A031C006_170718_R1W0 
1 1 10 A031C013_170718_R1W0 
undefined undefined undefined A001C549_190226_R04Q 
5 5 1 A015C005_170716_R1W0 
5 5 9 A024C006_170717_R1W0 
3 3 7 A002C003_170714_R1W0 
5 5 5 A021C005_170716_R1W0 
1 1 3 A004C006_170714_R1W0 
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*

Chrome сортирует по-разному:

5 5 1 A015C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
1 1 3 A004C006_170714_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
8 8 4 A020C004_170716_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*

Цель:

1 1 3 A004C006_170714_R1W0 
1 1 4 A031C006_170718_R1W0 
1 1 10 A031C013_170718_R1W0 
1-b 1 3 A004C007_170718_R1W0*  
2 2 1 A008C010_170715_R1W0
3 3 7 A002C003_170714_R1W0 
5 5 1 A015C005_170716_R1W0 
5 5 5 A021C005_170716_R1W0 
5 5PU 6 A021C005_170716_R1W0*
5 5 9 A024C006_170717_R1W0 
8 8 4 A020C004_170716_R1W0 
undefined undefined undefined A001C549_190226_R04Q 

Как мне отсортировать то, что я хочу?

ОБНОВЛЕНИЕ:
Я включил крайние случаи:

  • Сцена "1-b" добавленные символы "-b" должны быть отсортированы после всех элементов ссцена «1», когда ключ «сцена».

  • выстрел «5PU» добавленные символы «PU» следует игнорировать, когда ключ «выстрел» или «взять».

Ответы [ 3 ]

2 голосов
/ 30 апреля 2019

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

var options = { numeric: true, sensitivity: 'base', ignorePunctuation: true },
    array = [{ scene: "1", shot: "1", take: "4", namw: "A031C006_170718_R1W0" }, { scene: "8", shot: "8", take: "4", name: "A020C004_170716_R1W0" }, { scene: "1", shot: "1", take: "10", name: "A031C013_170718_R1W0" }, { scene: undefined, shot: undefined, take: undefined, name: "A001C549_190226_R04Q" }, { scene: "2", shot: "2", take: "1", name: "A008C010_170715_R1W0" }, { scene: "5", shot: "5", take: "1", name: "A015C005_170716_R1W0" }, { scene: "3", shot: "3", take: "7", name: "A002C003_170714_R1W0" }, { scene: "5", shot: "5", take: "5", name: "A021C005_170716_R1W0" }, { scene: "5", shot: "5", take: "9", name: "A024C006_170717_R1W0" }, { scene: "1", shot: "1", take: "3", name: "A004C006_170714_R1W0" }, { scene: "1-b", shot: "1", take: "3", name: "A004C007_170718_R1W0*" }, { scene: "5", shot: "5PU", take: "6", name: "A021C005_170716_R1W0*" }],
    keys = ["scene", "shot", "take", "name"];

array.sort((a, b) => {
    var result;
    keys.some(k => result = String(a[k]).localeCompare(b[k], undefined, options));
    return result;
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 30 апреля 2019

Это сложный вопрос.Сначала код, потом объяснения:

const scenes = [
  {"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
  {"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
  {"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
  {"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
  {"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
  {"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
  {"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
  {"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
  {"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
  {"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
]

const compareString = (a, b) => {
  if (`${a}` !== a) return 1
  if (`${b}` !== b) return -1
  return a.localeCompare(b, undefined, {
    numeric: true,
    sensitivity: 'base',
    ignorePunctuation: true
  })
}

const sorts = [
  'scene',
  'shot',
  'take',
  'name',
]

const globalCompare = (a, b) =>
  sorts.reduce((acc, key) => {
    if (acc == 0) return compareString(a[key], b[key])
    return acc
  }, 0)

scenes.sort(globalCompare)

console.log(scenes)

Пояснения:

1- Функция сравнения принимает два аргумента (традиционно a и b) и возвращаетцелое число, которое определяет, какой из них должен быть «перед» другим.Целое число выглядит следующим образом: 1, если a должно быть до, чем b, -1, если оно должно быть после, и 0, если они эквивалентны.

2- Нам нужно убедиться, чточто «второй порядок» должен быть только сортировать эквивалент объектов!(Вот почему мы используем reduce())

0 голосов
/ 30 апреля 2019

Ваши функции сортировки нестабильны.

  if (!v1.shot) return -1;

Что, если v2 также не имеет свойства "выстрел"?Ваша функция сортировки всегда должна возвращать противоположный результат для sort(a, b) и sort(b, a), а ваша - нет.Поскольку Chrome, похоже, использует другой вид сортировки, он не работает только для этого браузера (в настоящее время алгоритмы могут измениться в будущем).

...