如何根据JavaScript中的“之前”和“之后”条件对数组进行排序?

时间:2015-12-06 14:04:10

标签: javascript sorting

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

示例:

  • 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搜索。

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

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

编辑3 :我正在考虑实现这一点的方法是计算所有可能的组合,然后寻找满足所有条件的第一个组合。这对我来说可能不是非常慢,因为在我的情况下,我只能在应用程序启动时执行此操作,并且我不会在阵列中拥有超过50个项目。但无论如何,对于科学来说,了解更优化的解决方案会更好。

编辑4 :我会接受仅在条件之后使用的解决方案。与MS Project一样。

4 个答案:

答案 0 :(得分:1)

  

奖励:这种排序的名称是否优于"条件排序"?

它被称为topological sort。根据您的确切输入格式和内容,它甚至可能没有明确定义。

答案 1 :(得分:1)

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

(这个答案是我对这个问题的回答的一部分: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>');

编辑4 的解决方案,仅条件后的

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

答案 2 :(得分:0)

这可能有用。

&#13;
&#13;
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']
&#13;
&#13;
&#13;

答案 3 :(得分:0)

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

&#13;
&#13;
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>");
&#13;
&#13;
&#13;

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