JavaScript: алгоритм сортировки, основанный на предопределенном порядке регулярных выражений - PullRequest
0 голосов
/ 09 января 2019

Я хочу отсортировать массив размеров одежды на основе предварительно определенного порядка регулярных выражений.

Массив выражений выглядит так:

const sizeOrder = [
  /One Size/,
  /[X/S]+S/i,
  /^S$/i,
  /^M$/i,
  /^L$/i,
  /[X]+L/i,
  /[4-9]XL/i,
  /[0-9,]+/,
];

Какой был бы аккуратный и эффективный способ сортировки массива, который, например, выглядел бы так:

const sizes = [
  '45,5',
  'S',
  'XXS',
  'XXL',
  'XS',
  '4XL',
  'One Size',
  '0',
  '32',
  '42,5',
  '18',
  'XXS/XS',
  'XXXS',
  'L'
];

В качестве первого шага я бы создал сегменты для соответствующих регулярных выражений, и если совпадение существует, я помещаю это значение в сегмент, например, так:

function exampleSort() {
  const bucket = Array.from(new Array(sizeOrder.length), () => []);

  sizes.forEach(size => {
    const i = sizeOrder.findIndex(order => order.test(size));

    if (i > -1) {
      bucket[i].push(size);
    }
  });
}

После этого я бы просматривал каждое ведро и сортировал их соответствующим образом, а затем соединял эти массивы в одно.

Но у меня есть два вопроса:

А как насчет особого случая XXS/XS? Как бы я отсортировал это ведро так, чтобы XXS/XS было между XXS и XS?

Кажется, это обязательная и наивная реализация. Есть ли другой способ сделать это более эффективно?

Это мой ожидаемый результат:

const sorted = [
  'One Size',
  'XXXS',
  'XXS',
  'XXS/XS',
  'XS',
  'S',
  'L',
  'XXL',
  '4XL',
  '0',
  '18',
  '32',
  '42,5',
  '45,5'
];

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Вы можете использовать поэтапный подход, отметив

  • если строка содержит косую черту, то разбить эту строку и принять для нее среднее значение,

  • если в строке есть другие шаблоны, то вернуть значение, отражающее порядок или вместе со смещением значение для 'S', 'M' 'L',

  • , если строка содержит некоторое начальное значение 'X' или цифру, за которой следует 'S' или 'L', тогда получите число или число начальных значений 'X' и используйте коэффициент для перемещения значения в правильное направление.

const
    getOrder = s => {
        var standard = { s: -0.5, m: 0, l: 0.5 },
            x = 0;
        if (s.includes('/')) return s.split('/').map(getOrder).reduce((a, b) => a + b) / 2;
        if (/^One Size$/.test(s)) return 1;
        if (/^[lms]$/i.test(s)) return standard[s.toLowerCase()] + 2;
        if (/x+[sl]$/i.test(s)) {
            if (/^\dx/i.test(s)) {
                x = s[0];
            } else {
                while (s[x].toLowerCase() === 'x') x++;
            }
            return standard[s.slice(-1).toLowerCase()] * (1 + 0.01 * x) + 2;
        }
        if (/^[0-9,]+$/.test(s)) return 3;
        return 0;
    },
    sizes = ['45,5', 'S', 'XXS', 'XXL', 'XS', '4XL', 'One Size', '0', '32', '42,5', '18', 'XXS/XS', 'XXXS', 'L', 'M'];

sizes.sort((a, b) => getOrder(a) - getOrder(b) || a.replace(',', '.') - b.replace(',', '.'));

console.log(sizes);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 09 января 2019

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

Обратите внимание на добавление якорей ^ и $, чтобы гарантировать, что только полные совпадения соответствуют .test.

const sizeOrder = [
  /^One Size$/,
  /^[X/S]+S$/i,
  /^S$/i,
  /^M$/i,
  /^L$/i,
  /^[X]+L$/i,
  /^[4-9]XL$/i,
  /^[0-9,]+$/,
];
const patternMap = new Map(sizeOrder.map(pattern => [pattern, []]));
const sizes = [
  '45,5',
  'S',
  'XXS',
  'XXL',
  'XS',
  '4XL',
  'One Size',
  '0',
  '32',
  '42,5',
  '18',
  'XXS/XS',
  'XXXS',
  'L'
];
sizes.forEach((str) => {
  const matchingPattern = sizeOrder.find(pattern => pattern.test(str));
  patternMap.get(matchingPattern).push(str);
});
const valuesArr = [...patternMap.values()];
valuesArr.forEach(arr => arr.sort((a, b) => b.localeCompare(a)));
// sort the last one from /^[0-9,]+$/ differently:
valuesArr[valuesArr.length - 1].sort();
console.log(valuesArr.flat());

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

const sizeOrder = [
  { pattern: /^One Size$/ },
  { pattern: /^[X/S]+S$/i, }, // insert your custom sort logic for XXS/XS here
  { pattern: /^S$/i },
  { pattern: /^M$/i },
  { pattern: /^L$/i },
  { pattern: /^[X]+L$/i },
  { pattern: /^[4-9]XL$/i },
  { pattern: /^[0-9,]+$/, sort: arr => arr.sort() }
];
const patternMap = new Map(sizeOrder.map(({ pattern }) => [pattern, []]));
const sizes = [
  '45,5',
  'S',
  'XXS',
  'XXL',
  'XS',
  '4XL',
  'One Size',
  '0',
  '32',
  '42,5',
  '18',
  'XXS/XS',
  'XXXS',
  'L'
];
sizes.forEach((str) => {
  const { pattern } = sizeOrder.find(({ pattern }) => pattern.test(str));
  patternMap.get(pattern).push(str);
});
const valuesArr = [...patternMap.values()];
valuesArr.forEach((arr, i) => {
  const sort = sizeOrder[i].sort;
  if (sort) {
    sort(arr);
  } else {
    arr.sort((a, b) => b.localeCompare(a));
  }
});
console.log(valuesArr.flat());
...