按模式排序数组,保留模式中未找到的元素的顺序

时间:2018-07-19 12:29:21

标签: javascript arrays algorithm sorting

如何通过模式对数组进行排序,以使模式中不存在的元素不会移动到数组的末尾,但保留其顺序?

有一个例子:

let input = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Orange', 'Peach']
let pattern = ['Orange', 'Peach', 'Banana', 'Grape']

// desired output
let output = ['Apple', 'Orange', 'Peach', 'Banana', 'Cherry', 'Grape', 'Mango']

图案的长度可以等于,小于或大于输入数组的长度。

标准排序:

input.sort( (a, b) => {
    let indexA = pattern.indexOf(a);
    let indexB = pattern.indexOf(b);
    return Math.sign(indexA - indexB);
});

// result:
// Apple,Cherry,Mango,Orange,Peach,Banana,Grape
// elements that are not present in pattern have been moved to the left

但是,我们要保留模式阵列中不存在的元素的相对位置。最有效的方法是什么?

使用示例:对表列重新排序(有些列可能被隐藏,但我们记住它们的顺序)。

3 个答案:

答案 0 :(得分:1)

您可以为索引获取一个对象,并在一个数组中获取所需模式的所有索引,对其进行排序并映射原始数组,然后获取该模式的项目(已排序的项目)。

var input = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Orange', 'Peach'],
    pattern = ['Orange', 'Peach', 'Banana', 'Grape'],
    patternO = Object.assign(...pattern.map((k, v) => ({ [k]: v }))),
    indices = [...input.keys()]
        .filter(i => input[i] in patternO)
        .sort((a, b) => patternO[input[a]] - patternO[input[b]]),
    result = input.map((j => (v, i) => v in patternO ? input[indices[j++]]: v)(0));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:1)

patternElementIndexTable只是为了加快某些操作的速度,可以将其删除,然后将indexOf放在相关位置。这将降低算法的顺序并在输入为例如大于几千个项目。

首先,仅考虑input中出现的pattern的元素,并创建该列表和input之间的索引的映射:

input.map((_, i) => i).filter(i => input[i] in patternElementIndexTable)

然后,我根据映射将分配映射到intermediary的任何整数索引,以更改input的相关属性。这基本上意味着在intermediary上进行操作就像在input上进行操作,而只是考虑了所需的元素。

因此,对中介物进行排序即可达到预期的效果:

let input = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Orange', 'Peach'];
let pattern = ['Orange', 'Peach', 'Banana', 'Grape'];

let patternElementIndexTable = pattern.reduce((p, c, i) => (p[c] = i, p), {});

let intermediary = new Proxy(
  Object.seal(input.map((_, i) => i).filter(i => input[i] in patternElementIndexTable)),
  {
    get: (target, prop) => {
      if (Number.isInteger(+prop) && +prop >= 0) {
        if (+prop >= target.length)
          return undefined;
        return input[target[prop]];
      }
      return target[prop];
    },
    set: (target, prop, value) => {
      if (Number.isInteger(+prop) && +prop >= 0) {
        if (+prop >= target.length) {
          return false;
        }
        input[target[prop]] = value;
        return true;
      }
      return false;
    }
  }
);

intermediary.sort((a, b) => patternElementIndexTable[a] - patternElementIndexTable[b]);

console.log(input);

我首先想到的是重新实现某种添加了该功能的排序算法,但是这种方法似乎更加通用(对于其他不会改变长度的变异操作也应如此,例如reverse,{{1 }}或自定义函数)。

请注意,仅更改fill的人不应该使用intermediary。添加或删除元素是无效的操作。非整数索引属性的任何更改都是无效操作。

答案 2 :(得分:0)

您可以循环input数组,并在pattern数组中每次出现的元素出现时增加一个计数器变量,并使用具有该索引的元素。

let input = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Orange', 'Peach']
let pattern = ['Orange', 'Peach', 'Banana', 'Grape'];

let current = 0;
input.forEach((e, i) => {
  if (pattern.includes(e)) input[i] = pattern[current++]
})

console.log(input)