How to sort based on incomplete criteria?

前端 未结 3 1518
旧巷少年郎
旧巷少年郎 2021-01-15 22:05

First I tried passing my own function to Array.sort, but it doesn\'t sort correctly. Notice how \'c\' comes before \'a\' in the resul

相关标签:
3条回答
  • 2021-01-15 22:49

    Maybe something like this

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

    0 讨论(0)
  • 2021-01-15 22:57

    The algorithm in the answer I gave earlier on, and which you (first) accepted, is really based on a heuristic.

    For a sorted output to be guaranteed to not have any violations, you could treat this problem as a graph problem. Whenever two values can make a comparison that gives true (with either comparator function), then that pair represents an edge in the graph.

    If the order is consistent, then there must be one value that is the least among the others, otherwise you would have a cycle.

    So with that knowledge we can determine for each node in the graph how long the longest path is to such a least node. When you find the longest distance to such a least node, you can use the length of that path as an absolute order indication.

    Here is an implementation:

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

    0 讨论(0)
  • 2021-01-15 23:00
    const unsorted = ['c', 'd', 'a', 'b'];
    const sorted = unsorted.sort();
    

    It should work I'm not sure what's your issue.

    0 讨论(0)
提交回复
热议问题