如何使用有时无法比较的值对数组进行排序?

时间:2014-03-24 20:52:59

标签: javascript node.js sorting

我正在尝试对有时彼此无法比拟的值进行排序。我正在排序的数据类似于树

1. if a depends on b, then a should be sorted after b
2. if b depends on a, then b should be sorted after a
3. if neither a or b depend on each other then they are incomparable and no comparison can be drawn

我目前在javascript中使用自定义比较功能的sort函数。但我怀疑它不合适,因为我基本上需要Topological Sorting

带测试的示例代码:

// k depends on nothing - but every other element depends on k, so k should come first
var k = {
  uuid: 'k',
  dependsOn: []
};

// z depends on k and so k must come before z
var z = {
  uuid: 'z',
  dependsOn: ['k']
}

// y should be go before x as x depends on y
// y also depends on k so k should go before y
var y = {
  uuid: 'y',
  dependsOn: ['k']
}

// x has both y and k as its dependencies
var x = {
  uuid: 'x',
  dependsOn: ['y', 'k']
}

function compare(a, b) {
  // if they have the same uuid; then they are the same
  if (a.uuid === b.uuid) {
    return 0
  }

  // if a depends on b, then a should be after b
  for (var i = 0, len = a.dependsOn.length; i < len; i++) {
    var dependsOn = a.dependsOn[i];

    if (dependsOn === b.uuid) {
      return 1
    }
  }

  // if b depends on a, then b should be after a
  for (var i = 0, len = b.dependsOn.length; i < len; i++) {
    var dependsOn = b.dependsOn[i];

    if (dependsOn === a.uuid) {
      return -1
    }
  }

  // this is the edge case, 
  // if neither a or b depends on each other, then they don't have relative ranking
  return null
}

// this is every possible permutation - they should all sort to the same orders
// expected order k, z, y, x or k, y, z, x or k, y, x, z
// because:
// k -> z  as z depends on k
// k -> y  as y depends on k
// no relative ranking between z and y as they don't depend on each other
// x depends on both y and k so x will come after them
var perms = [
  [x, y, z, k],
  [x, y, k, z],
  [x, z, y, k],
  [x, z, k, y],
  [x, k, y, z],
  [x, k, z, y],
  [y, x, z, k],
  [y, x, k, z],
  [y, z, x, k],
  [y, z, k, x],
  [y, k, x, z],
  [y, k, z, x],
  [z, x, y, k],
  [z, x, k, y],
  [z, y, x, k],
  [z, y, k, x],
  [z, k, x, y],
  [z, k, y, x],
  [k, x, y, z],
  [k, x, z, y],
  [k, y, x, z],
  [k, y, z, x],
  [k, z, x, y],
  [k, z, y, x],
]

var _ = require('underscore')
perms.forEach(function(perm) {
  var s = perm.sort(compare)
  var p = _.pluck(s, 'uuid')
  console.log(p, _.isEqual(p, ['k', 'z', 'y', 'x']) || _.isEqual(p, ['k', 'y', 'z', 'x']) || _.isEqual(p, ['k', 'y', 'x', 'z']))
})

示例输出(带注释):

[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'x', 'z', 'y' ] false \\ x depends on k and y so it should be after y
[ 'k', 'x', 'z', 'y' ] false
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'x', 'z', 'y' ] false \\ x depends on k and y so it should be after y
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'y', 'z', 'x' ] true
[ 'k', 'y', 'z', 'x' ] true
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'y', 'z', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'x', 'z', 'y' ] false \\ x depends on k and y so it should be after y
[ 'k', 'y', 'x', 'z' ] true
[ 'k', 'y', 'z', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true
[ 'k', 'z', 'y', 'x' ] true

2 个答案:

答案 0 :(得分:0)

我找到了一个看似不是非常积极维护的图书馆来完成这项工作:

toposort

// k depends on nothing - but every other element depends on k, so k should come first
var k = {
  uuid: 'k',
  dependsOn: []
};

// z depends on k and so k must come before z
var z = {
  uuid: 'z',
  dependsOn: ['k']
}

// y should be go before x as x depends on y
// y also depends on k so k should go before y
var y = {
  uuid: 'y',
  dependsOn: ['k']
}

// x has both y and k as its dependencies
var x = {
  uuid: 'x',
  dependsOn: ['y', 'k']
};

function Edges(array) {
  var edges = []
  for (var k in array) {
    var node = array[k]

    node.dependsOn.forEach(function(dependency) {
      edges.push([dependency].concat(node.uuid))
    })
  }

  return edges
}

// this is every possible permutation - they should all sort to the same orders
// expected order k, z, y, x or k, y, z, x or k, y, x, z
// because:
// k -> z  as z depends on k
// k -> y  as y depends on k
// no relative ranking between z and y as they don't depend on each other
// x depends on both y and k so x will come after them
var perms = [
  [x, y, z, k],
  [x, y, k, z],
  [x, z, y, k],
  [x, z, k, y],
  [x, k, y, z],
  [x, k, z, y],
  [y, x, z, k],
  [y, x, k, z],
  [y, z, x, k],
  [y, z, k, x],
  [y, k, x, z],
  [y, k, z, x],
  [z, x, y, k],
  [z, x, k, y],
  [z, y, x, k],
  [z, y, k, x],
  [z, k, x, y],
  [z, k, y, x],
  [k, x, y, z],
  [k, x, z, y],
  [k, y, x, z],
  [k, y, z, x],
  [k, z, x, y],
  [k, z, y, x],
]

var _ = require('underscore')
var toposort = require('toposort')
perms.forEach(function(perm) {
  var p = toposort(Edges(perm));
  console.log(p, _.isEqual(p, ['k', 'z', 'y', 'x']) || _.isEqual(p, ['k', 'y', 'z', 'x']) || _.isEqual(p, ['k', 'y', 'x', 'z']))
})

答案 1 :(得分:-1)

如果你需要拓扑排序的行为,我在github上的图形js库确实有这种类型的实现:

https://github.com/chen0040/js-graph-algorithms

图表算法库中进行顶级排序的示例代码如下:

var jsgraphs = require('js-graph-algorithms');

var dag = new jsgraphs.DiGraph(7); 

dag.addEdge(0, 5); // edge point from vertex 0 to 5
dag.addEdge(0, 2);
dag.addEdge(0, 1);
dag.addEdge(3, 6);
dag.addEdge(3, 5);
dag.addEdge(3, 4);
dag.addEdge(5, 4);
dag.addEdge(6, 4);
dag.addEdge(6, 0);
dag.addEdge(3, 2);
dag.addEdge(1, 4);

var ts = new jsgraphs.TopologicalSort(dag);

var order = ts.order();
console.log(order); // display array which is the topological sort order

var dag = new jsgraphs.DiGraph(7); dag.addEdge(0, 5); // edge point from vertex 0 to 5 dag.addEdge(0, 2); dag.addEdge(0, 1); dag.addEdge(3, 6); dag.addEdge(3, 5); dag.addEdge(3, 4); dag.addEdge(5, 4); dag.addEdge(6, 4); dag.addEdge(6, 0); dag.addEdge(3, 2); dag.addEdge(1, 4); var ts = new jsgraphs.TopologicalSort(dag); var order = ts.order(); console.log(order); // display array which is the topological sort order