Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 673
MHogge Avatar asked Dec 04 '22 19:12

MHogge


1 Answers

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(','));
like image 164
ponury-kostek Avatar answered Dec 06 '22 10:12

ponury-kostek