Search code examples
javascriptjqueryarraysmultidimensional-arrayunique

find unique entries inside a multidimensional javascript array, order important dependent on level


What would be the most elegant solution to find all unique first level entries inside a multidimensional javascript array? There is only one important rule: the order of the entries is important only on the first level, but not important on the second level

For example, for the following array the script should return 4 unique entries (the first, the third, the fourth and the fifth):

[
  [ [],[22],[1,13,17],[12],[] ], 
  [ [],[22],[17,13,1],[12],[] ], 
  [ [],[12],[1,13,17],[22],[] ], 
  [ [11],[12],[13],[14],[15] ], 
  [ [15],[14],[13],[12],[11] ]
]

PS. jQuery can be used as well.


Solution

  • If you're not all that worried about performance and just need something that works, you could use the constant depth you mentioned along with the string representation as a "fingerprint" of sorts (akin to Java's hashcode).

    Then you use a Set to keep track of items you've not seen before, and add only those that are new.

    function getUnique(rows) {
      let unique = new Set();
      let results = [];
      for (let row of rows) {
        // Fingerprint is the string representation of the row,
        // with the inner-level sorted (as order doesn't matter).
        // E.g., fingerprint of [ [8], [3, 2, 1], [9] ] is '[[8],[1,2,3],[9]]'
        let fingerprint = JSON.stringify(row.map((cells) => {
          return cells.concat().sort();  // Use concat to avoid sorting in place.
        }));
    
        // If we haven't seen this fingerprint before,
        // add to the filter and the results list.
        if (!unique.has(fingerprint)) {
          unique.add(fingerprint);
          results.push(row);
        }
      }
      return results;
    }
    

    This, for example, will come up with...

    > x = [
    ... [ [8], [3, 2, 1], [9] ],
    ... [ [7], [8, 3, 9], [1, 2] ],
    ... [ [8], [1, 2, 3], [9] ],
    ... ];
    > getUnique(x);
    [ [ [ 8 ], [ 3, 2, 1 ], [ 9 ] ],
      [ [ 7 ], [ 8, 3, 9 ], [ 1, 2 ] ] ]
    

    Obviously if your inner values are non-primitives (objects, arrays, etc) then this will fall over, but if you're dealing with numbers like your example, it should be fine.