【问题标题】:How to sort based on incomplete criteria?如何根据不完整的标准进行排序?
【发布时间】:2020-08-05 05:35:38
【问题描述】:

首先我尝试将自己的函数传递给Array.sort,但它没有正确排序。注意'c' 在结果中是如何出现在'a' 之前的,即使if (b == 'a' && a == 'c') 的情况处理正确。

这些数据只是举例。我的实际数据不按字母顺序排序。它必须使用a_before_bb_before_a 函数中说明的逻辑。

由于我只有条件来确定某些(不是全部)元素对的相对排序,因此可能存在多个有效的元素排序。我只需要生成任何有效的排序,其中有效的方式不与我的任何条件(在a_before_bb_before_a 函数中定义)相矛盾。

const sorted = ['a', 'b', 'c', 'd']; // I do NOT have access to this
const unsorted = ['c', 'd', 'a', 'b'];

const a_before_b = (a, b) => {
  if (a == 'a' && b == 'd') return true;
  if (a == 'b' && b == 'c') return true;
}

const b_before_a = (a, b) => {
  if (b == 'a' && a == 'c') return true;
  if (b == 'b' && a == 'c') return true;
}

const mySortingFunction = (a, b) => {
  if (a_before_b(a, b)) return -1;
  if (b_before_a(a, b)) return 1;
  return 0;
}

// doesn't produce correct sorting 
console.log(unsorted.sort(mySortingFunction)); // [ 'c', 'a', 'd', 'b' ]

然后我尝试从头开始编写自己的排序。但它进入了一个无限循环,我不知道为什么。

const sorted = ['a', 'b', 'c', 'd'];
const unsorted = ['c', 'd', 'a', 'b'];

const a_before_b = (a, b) => {
  if (a == 'a' && b == 'd') return true;
  if (a == 'b' && b == 'c') return true;
}

const b_before_a = (a, b) => {
  if (b == 'a' && a == 'c') return true;
  if (b == 'b' && a == 'c') return true;
}

const findAnUnsortedElement = array => {
  for (let [i, element] of Object.entries(array)) {
    i = +i;
    const a = element;
    const b = array[i + 1];
    if (b === undefined) return 'SORTING_COMPLETE';
    if (!a_before_b(a, b)) console.log(a, 'should not be before', b);
    if (b_before_a(a, b)) console.log(b, 'should be before', a);
    if (!a_before_b(a, b) || b_before_a(a, b)) return a;
  }
}

// from w3schools
function move(arr, old_index, new_index) {
  while (old_index < 0) {
    old_index += arr.length;
  }
  while (new_index < 0) {
    new_index += arr.length;
  }
  if (new_index >= arr.length) {
    var k = new_index - arr.length;
    while ((k--) + 1) {
      arr.push(undefined);
    }
  }
  arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
  return arr;
}

// enters infinite loop, never returns
const myCustomSort = array => {
  while (findAnUnsortedElement(array) != 'SORTING_COMPLETE') {
    const element = findAnUnsortedElement(array);
    const index = array.findIndex(el => el == element);
    console.log('moving', element);
    array = move(array, index, index + 1);
    console.log(array);
  }
  return array;
}

console.log(myCustomSort(unsorted));

【问题讨论】:

    标签: javascript arrays string sorting


    【解决方案1】:
    const unsorted = ['c', 'd', 'a', 'b'];
    const sorted = unsorted.sort();
    

    它应该可以工作我不确定你的问题是什么。

    【讨论】:

    • 也许在示例中使用字母是一个糟糕的选择。但是我的实际数据不是字母,不能用Array.sort
    • 我必须使用我在a_before_bb_before_a中指定的条件。
    • 实际数据是多少?
    • 实际数据是一个字符串列表,仅通过查看字符串根本无法推断其排序。字符串本身不包含有关其排序的信息。这就是为什么我没有在这里分享它们。
    • 由于我无法访问预先排序的列表,因此我不知道预先排序应该是什么样子。所以我不能添加更多的 if 语句。我没有了。我只需要得到一个不与任何 if 语句相矛盾的排序。
    【解决方案2】:

    我之前给出的答案中的算法,您(首先)接受的算法实际上是基于启发式的。

    为了保证排序后的输出没有任何违规,您可以将此问题视为图形问题。只要两个值可以进行比较,得到true(使用任一比较器函数),则该对表示图中的一条边。

    如果顺序一致,那么必须有一个值是最小的,否则就会出现循环。

    因此,有了这些知识,我们就可以确定图中每个节点到这样一个最小节点的最长路径有多长。当您找到到这样一个最小节点的最长距离时,您可以使用该路径的长度作为绝对顺序指示。

    这是一个实现:

    class Node {
        constructor(value) {
            this.value = value;
            this.prev = new Set;
            this.order = 0; // No order yet
        }
        orderWith(other) {
            if (other === this) return;
            if (a_before_b(this.value, other.value) || b_before_a(other.value, this.value)) {
                other.prev.add(this);
            } else if (a_before_b(other.value, this.value) || b_before_a(this.value, other.value)) {
                this.prev.add(other);
            }
        }
        setOrder(path = new Set) {
            // Use recursion to find length of longest path to "least" node.
            if (this.order) return; // already done
            if (path.has(this)) throw "cycle detected";
            let order = 1;
            for (let prev of this.prev) {
                prev.setOrder(path.add(this));
                order = Math.max(order, prev.order + 1);
            }
            this.order = order; // If order is 1, it is a "least" node
        }
    }
    
    const a_before_b = (a, b) => {
      if (a == 'a' && b == 'd') return true;
      if (a == 'b' && b == 'c') return true;
    }
    
    const b_before_a = (a, b) => {
      if (b == 'a' && a == 'c') return true;
      if (b == 'b' && a == 'c') return true;
    }
    
    function mySort(arr) {
        // Create a graph: first the nodes
        let nodes = {}; // keyed by values in arr
        for (let value of arr) nodes[value] = nodes[value] || new Node(value);
    
        // Then the edges...
        for (let i = 0; i < arr.length; i++) {
            for (let j = i+1; j < arr.length; j++) {
                nodes[arr[i]].orderWith(nodes[arr[j]]);
            }
        }
        
        // Set absolute order, using the longest path from a node to a "least" node.
        for (let node of Object.values(nodes)) node.setOrder();
        
        // Sort array by order:
        return arr.sort((a, b) => nodes[a].order - nodes[b].order);
    }
    
    const sorted = ['a', 'b', 'c', 'd'];
    const unsorted = ['c', 'd', 'a', 'b'];
    console.log(mySort(unsorted));

    【讨论】:

    • 您介意分享一些好的资源吗?在哪里可以阅读更多关于这种方法的信息?
    • 我也将在我的数据上尝试这个功能。如果有循环,我喜欢它似乎会引发错误;我也真的需要这个功能。您可以在另一个问题中看到我使用的实际数据:stackoverflow.com/q/63260287/13378247
    • 这确实是一个depth-first topological sort algorithm 的实现,虽然它不会在算法过程中构建列表,而是在单独的sort 中使用“order”属性来执行此操作。
    • 这太棒了!我还可以使用它来确定哪些节点具有相同的顺序,这是我没有要求但确实需要的。
    • P.S.整整一年,我一直在寻找能做到这一点的东西。再次感谢。
    【解决方案3】:

    可能是这样的

    const sorted = ['a', 'b', 'c', 'd']; // I do NOT have access to this
    const unsorted = ['c', 'd', 'a', 'b'];
    
    const a_before_b = (a, b) => {
      if (a == 'a' && b == 'd') return true;
      if (a == 'b' && b == 'c') return true;
      if (a == 'a' && b == 'c') return true;
    
    }
    
    const b_before_a = (a, b) => {
      if (b == 'a' && a == 'c') return true;
      if (b == 'b' && a == 'c') return true;
    }
    
    const mySortingFunction = (a, b) => {
      if (a_before_b(a, b)) return -1;
      if (b_before_a(a, b)) return 1;
      return 0;
    }
    
    // doesn't produce correct sorting 
    console.log(unsorted.sort(mySortingFunction));

    【讨论】:

    • 感谢您为回答我的问题所做的工作。不幸的是,我不够清楚。我实际上无权访问sorted 列表。我事先不知道排序列表是什么样的。而且我不具备所有条件。一些。根据我所掌握的少量信息,我需要将列表排序为任何订单(不管是哪一个),只要生成的订单不与我的任何条件相矛盾。您的答案“作弊”,因为您根据预先排序的列表推断出所有条件。
    猜你喜欢
    • 1970-01-01
    • 2012-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2021-05-08
    • 1970-01-01
    • 2015-01-20
    相关资源
    最近更新 更多