无序数组判断内容相等

最近开发一个功能,使用 Mysql 存储的数据,数据源是 Elactic Search,每天凌晨获取一下当前某个项目的性能指标的平均值。

由于提供了 URL筛选的范围,所以对于查询条件出现了排列组合的方式,类似下面这样的。

const params = [{ path: 'a.com', filter: 0 }, { path: 'b.com', filter: 1 }]; // 查询条件

先是计算出全组合,这里有一个算法:

/*
  全组合算法
  输入 [a,b,c,d,e]
  输出 [Array(5), Array(10), Array(10), Array(5), Array(1)]
  展开后得
  ['A', 'B', 'C', 'D', 'E']
  ['AB', 'AC', 'BC', 'AD', 'BD', 'CD', 'AE', 'BE', 'CE', 'DE']
  ['ABC', 'ABD', 'ACD', 'BCD', 'ABE', 'ACE', 'BCE', 'ADE', 'BDE', 'CDE']
  ['ABCD', 'ABCE', 'ABDE', 'ACDE', 'BCDE']
  ['ABCDE']
 */
Helper.combination = (arr) => {
  const list = [];
  for (let i = 0; i < arr.length; i++) {
    list.push([]);
  }

  const l = Math.pow(2, arr.length) - 1;

  for (let i = 1; i <= l; i++) {
    const t = [];

    for (let s = i, k = 0; s > 0; s >>= 1, k++) {
      if ((s & 1) === 1) {
        t.push(arr[k]);
      }
    }

    list[t.length - 1].push([t]); // join 的分隔符可以自行约定
  }

  return list.flat(2); // 拍平
};

因为我的查询条件是一个对象数组,最小的查询单元也是一个对象,如果要把这个对象当成一个查询维度存储到数据库中,首先想到的是 Hash,我首先想到了用 MD5 hash 一下 JSON.strigify([{}])后的内容,然而在查询的时候却无法查询到。

此时我发现请求的参数中数组的内元素顺序会发生改变,虽然内容不变,但是顺序变换之后,Hash 的结果也因此发生改变,所以需要先调整数组的位置,形成一个“稳定的”结构后再 Hash 存储。

所以就转变为了字符串的比较,通过调整顺序,最终形成的字符串一定是一样的。

/* 字符串排序 */
const sort = (str) => {
  const strArr = str.split('');
  const sorted = strArr.sort((a, b) => a.localeCompare(b)); // 转换成字符串排序
  return sorted.join(); // 变成字符串用于 Hash
};

这里我通过 sort 方法来比较字符之前的顺序,从而达到调整位置的目的。其他同事有提到 charCodeAt 转变为数字,再排序形成字符串的形式,不过这种方式本质都是一样的,从乱序到有序的过程。

Finish!