【问题标题】:How can I sort an array based on "before" and "after" conditions in JavaScript?如何根据 JavaScript 中的“之前”和“之后”条件对数组进行排序?
【发布时间】:2015-12-07 03:13:41
【问题描述】:

我想根据“之前”和“之后”条件对数组进行排序。

例子:

  • C should be before B:示例:{key: 'C', condition: {$before: 'B'}
  • B should be after A:示例:{key: 'B', condition: {$after: 'A'}
  • 答:例如{key: 'A'}

生成的排序列表将是:A, C, B

我需要这个,所以我可以对管道中的中间件列表进行排序。每个中间件都有一个条件,我应该提出一个满足所有中间件的所有条件的顺序。这类似于MS Project 在考虑到任务要求的情况下以甘特图组织任务。

问题:实现它的最简单方法是什么?即使使用像underscore 这样的外部库?奖励:这种排序的名称是否比“条件排序”更好?这将有助于我的 Google 搜索。

编辑:输入应该是包含条件的项目数组。我无法对排序方法进行硬编码。

EDIT 2:正如@Berdi 所说。这称为类型排序。是的,根据项目和条件,可能没有满足所有条件的组合,我的算法应该会触发异常。

编辑 3:我正在考虑的实现方式是计算所有可能的组合,然后寻找满足所有条件的第一个组合。这对我来说可能不是很慢,因为就我而言,我可以在应用程序启动时只执行一次,并且数组中的项目不会超过 50 个。但无论如何,对于科学来说,最好知道一个更优化的解决方案。

编辑 4:我会接受仅适用于 after 条件的解决方案。喜欢MS Project

【问题讨论】:

  • 您只是想对数组进行排序,对吗?

标签: javascript sorting


【解决方案1】:

奖励:这种排序的名称是否比“条件排序”更好?

它叫做topological sort。并且根据您的确切输入格式和内容,它甚至可能没有明确定义。

【讨论】:

  • 谢谢@Bergi。我不知道。是的!取决于我的物品和条件... sort 方法应该会触发异常。
  • @andrerpena:你能告诉我们你的“规则”的输入格式吗?只有这样,我们才能设计出一个正确而完整的答案。
  • 我会接受仅适用于$after 条件的答案。如果两者都很难获得
  • @andrerpena: x <before> y 表示y <after> x,所以转换起来很简单。
【解决方案2】:

我会使用:一个哈希表,一个开始的指针,然后重新组装数组。

(这个答案是我对这个问题的回答的一部分:Ordering array of objects that have a BeforeID and AfterID on each object

function chain(array) {
    var o = {}, pointer;
    array.forEach(function (a) {
        o[a.id] = a;
        if (a.beforeId === null) {
            pointer = a.id;
        }
    });
    array = [];
    do {
        array.push(o[pointer]);
        pointer = o[pointer].afterId;
    } while (pointer !== null);
    return array;
}

var unsorted = [{ id: 7, beforeId: 6, afterId: 8 }, { id: 11, beforeId: 10, afterId: null }, { id: 0, beforeId: null, afterId: 1 }, { id: 1, beforeId: 0, afterId: 2 }, { id: 4, beforeId: 3, afterId: 5 }, { id: 8, beforeId: 7, afterId: 9 }, { id: 2, beforeId: 1, afterId: 3 }, { id: 9, beforeId: 8, afterId: 10 }, { id: 10, beforeId: 9, afterId: 11 }, { id: 3, beforeId: 2, afterId: 4 }, { id: 5, beforeId: 4, afterId: 6 }, { id: 6, beforeId: 5, afterId: 7 }];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');

EDIT 4after 条件的解决方案。

function chain(array) {
    var n = {}, o = {}, pointer;
    array.forEach(function (a) {
        o[a.id] = a;
        n[a.after] = a.id;
        if (a.after === null) {
            pointer = a.id;
        }
    });
    // rewind pointer.
    // i took push for the array. otherwise the array could be mounted
    // from the other end with unshift but push is more efficient.
    do {
        pointer = n[pointer];
    } while (pointer in n);
    array = [];
    do {
        array.push(o[pointer]);
        pointer = o[pointer].after;
    } while (pointer !== null);
    return array;
}

var unsorted = [{ id: 7, after: 8 }, { id: 11, after: null }, { id: 0, after: 1 }, { id: 1, after: 2 }, { id: 4, after: 5 }, { id: 8, after: 9 }, { id: 2, after: 3 }, { id: 9, after: 10 }, { id: 10, after: 11 }, { id: 3, after: 4 }, { id: 5, after: 6 }, { id: 6, after: 7 }, ];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');

编辑 - 之前/之后稀疏

function chain(array) {
    var after = {}, before = {}, o = {}, pointer = array[0].id;
    array.forEach(function (a) {
        o[a.id] = a;
        if (a.after !== null) {
            after[a.id] = a.after;
            before[a.after] = a.id;
        }
        if (a.before !== null) {
            before[a.id] = a.before;
            after[a.before] = a.id;
        }
    });

    document.write('<pre>before ' + JSON.stringify(before, 0, 4) + '</pre>');
    document.write('<pre>after ' + JSON.stringify(after, 0, 4) + '</pre>');

    do {
        document.write('pointer: ' + pointer + '<br>');
        pointer = before[pointer];
    } while (pointer in before);
    document.write('pointer: ' + pointer + '<br>');
    array = [];
    do {
        array.push(o[pointer]);
        pointer = after[pointer];
    } while (pointer !== undefined);
    return array;
}

var unsorted = [{ id: 'C', before: 'B', after: null }, { id: 'B', before: null, after: null }, { id: 'A', before: null, after: 'B' }];
document.write('<pre>' + JSON.stringify(chain(unsorted), 0, 4) + '</pre>');

【讨论】:

  • 这看起来很有希望。我现在必须吃午饭,但我会先检查你的解决方案!提前谢谢你。
  • 感谢您的努力。我刚刚针对我的示例测试了您的解决方案...您的 do/while 正在第一次迭代中退出,因此生成的数组只有一个项目:jsfiddle.net/vasa2vx9/1
  • hm, var unsorted = [ { id: 'C', before: 'B', after: null }, { id: 'B', before: null, after: 'A'}, { id: 'A', before: null, after: null }]; 没有意义,因为 b 有两个孩子,但该解决方案适用于链,不适用于树。
  • 我明白了。是的,我的问题更多的是关于一棵树而不是一条链。谢谢!我感谢你的努力。我支持你接近:)
【解决方案3】:

这可能有用。

function sortByRules(rules, arr) {
    var rulesEvaluated = [];

    rules.forEach(rule => {
      applyRule(rule, arr);
      rulesEvaluated.push(rule);

      if (conflicts(rulesEvaluated, arr)){
        throw new Error('rule conflict');
      }
    });

    return arr;
}

function conflicts(rules, arr) {
    return rules.some(r => r.condition.before ?
      arr.indexOf(r.key)>arr.indexOf(r.condition.before) :
      arr.indexOf(r.key)<arr.indexOf(r.condition.after));
}

function applyRule(rule, arr) {
    var increment = rule.condition.before ? -1 : 1;
    var item = arr.splice(arr.indexOf(rule.key), 1)[0];
    var target = rule.condition.before || rule.condition.after;
    arr.splice(Math.max(0, arr.indexOf(target) + increment), 0, item);
}


var toSort = ['b', 'd', 'c', 'a'];    
var rules = [ { key: 'b', condition: { before: 'c' } }, 
              { key: 'd', condition: { after: 'c' } }, 
              { key: 'a', condition: { before: 'b' } } ];
document.write(sortByRules(rules, toSort)); // ['a', 'b',  'c', 'd']

【讨论】:

  • 这听起来像O(rules * items²) 真的很慢
【解决方案4】:

创建一个对象来存储你需要的订单,然后像往常一样使用[].sort()

var sorter = {
  'A': 1,
  'B': 3,
  'C': 2
}

var input = ['A', 'B', 'C'];

input = input.sort(function sort(a, b) {
  return sorter[a] > sorter[b];
});

console.log(input);
document.write("<pre>" + input + "</pre>");

现在,您可以对包含 A、B 和 C 任意组合的任何数组进行排序。 A, B, C 可以是任何东西,一个属性,它的长度,一个值的第一个字母......

【讨论】:

  • 创建一个对象来存储你需要的顺序”如果你只有“之前”和“之后”的规则,这实际上是困难的部分。
  • 如果规则来自你的老板/客户,除了手动构建这个对象别无选择。如果它们来自数据库、可解析文件等,则可能有一种以编程方式生成它的方法。
猜你喜欢
  • 2018-11-01
  • 2021-03-08
  • 2013-09-30
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
  • 2022-08-15
  • 2011-07-22
相关资源
最近更新 更多