代码之家  ›  专栏  ›  技术社区  ›  Johannes Klauß

javascript:基于预先定义的regex顺序排序的算法

  •  2
  • Johannes Klauß  · 技术社区  · 6 年前

    我想根据预先定义的正则表达式顺序对服装尺寸数组进行排序。

    表达式数组如下所示:

    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'
    ];
    

    作为第一步,我将为相应的正则表达式创建bucket,如果存在匹配项,我将该值推送到bucket,如下所示:

    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);
        }
      });
    }
    

    在这之后,我将遍历每个bucket并对它们进行相应的排序,然后将这些数组连接到一起。

    但我有两个问题:

    有什么特殊情况吗 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 回复  |  直到 6 年前
        1
  •  2
  •   CertainPerformance    6 年前

    一个选项是遍历字符串,将 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());
        2
  •  1
  •   Nina Scholz    6 年前

    你可以通过检查

    • 如果字符串包含斜杠,则拆分该字符串并取其平均值,

    • 如果字符串中有其他一些模式,则返回一个值,反映顺序或连同偏移量一起返回 '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; }