Search code examples
javascriptarraystopological-sort

Merge arrays and keep ordering


NOTE

The question has been edited following the good advise from @Kaddath to highlight the fact that the ordering doesn't have to be alphabetical but depending on the position of items inside the arrays.


I have an array of arrays where each of the arrays are based on a given ordering but they can differ a bit.

For example, the base ordering is X -> D -> H -> B and here is my array of arrays:

const arrays = [
  ['X', 'D', 'H', 'B'],
  ['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
  ['X', 'M', 'D', 'H', 'B'],
  ['X', 'H', 'T'],
  ['X', 'D', 'H', 'B']
]

I would like to merge all arrays into a single one and remove duplicates but by keeping the ordering. In my example the result would be ['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A'].

In the example we can see that M is between X and D inside the third array and it is so placed between X and D in the final output.

I know conflicts may arise but here are the following rules:

  • Every items should appear in the final output.
  • If an item is appearing in multiple arrays at different positions, the first appearance is the right one (skip others).

What I've done so far is merging all of these arrays into a single one by using

const merged = [].concat.apply([], arrays);

(cf. https://stackoverflow.com/a/10865042/3520621).

And then getting unique values by using this code snippet from https://stackoverflow.com/a/1584377/3520621 :

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
}; 
const finalArray = merged.unique();

But my result is this:

[
  "X",
  "D",
  "H",
  "B",
  "K",
  "Z",
  "A",
  "M",
  "T"
]

Any help is welcome!

Thanks.


Solution

  • const arrays = [
      ['X', 'D', 'H', 'B'],
      ['X', 'D', 'K', 'Z', 'H', 'B', 'A'],
      ['X', 'M', 'D', 'H', 'B'],
      ['X', 'H', 'T'],
      ['X', 'D', 'H', 'B']
    ];
    const result = [];
    arrays.forEach(array => {
      array.forEach((item, idx) => {
        // check if the item has already been added, if not, try to add
        if(!~result.indexOf(item)) {
          // if item is not first item, find position of his left sibling in result array
          if(idx) {
            const result_idx = result.indexOf(array[idx - 1]);
            // add item after left sibling position
            result.splice(result_idx + 1, 0, item);
            return;
          }
          result.push(item);
        }
      });
    });
    console.log('expected result', ['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A'].join(','));
    console.log(' current result',result.join(','));