Объединяйте массивы и продолжайте упорядочивать - PullRequest
0 голосов
/ 11 декабря 2018

ПРИМЕЧАНИЕ

Этот вопрос был отредактирован после полезного совета @Kaddath, чтобы подчеркнуть тот факт, что порядок не должен быть алфавитным, а зависит от положения элементов внутри массивов.


У меня есть массив массивов, где каждый из массивов основан на заданном порядке, но они могут немного отличаться.

Например, базовый порядок равен X -> D -> H -> B иВот мой массив массивов:

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']
]

Я хотел бы объединить все массивы в один и удалить дубликаты , но сохраняя ordering .В моем примере результат будет ['X', 'M', 'D', 'K', 'Z', 'H', 'T', 'B', 'A'].

. В примере мы можем видеть, что M находится между X и D внутри третьего массива, и поэтому он расположен между X иD в окончательном выводе.

Я знаю, что могут возникнуть конфликты, но вот следующие правила:

  • Каждый элемент должен появляться в окончательном выводе.
  • Если элемент появляется в нескольких массивах в разных позициях, первое появление является правильным (пропустите другие).

То, что я до сих пор делал, - это объединение всех этих массивов в один с помощью

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

(ср. https://stackoverflow.com/a/10865042/3520621).

Изатем получить уникальные значения, используя этот фрагмент кода из 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();

Но мой результат таков:

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

Любая помощь приветствуется!

Спасибо.

Ответы [ 9 ]

0 голосов
/ 11 декабря 2018

Забавная проблема для решения;Я думаю, что только частично добился успеха.

  • Я проигнорировал "недостаточно конкретный" пример B -> A -> T против T -> B -> A
  • Это очень неэффективно

Тем не менееопубликовать, потому что я думаю, что это может помочь вам сделать все правильно.Вот мой подход:

Шаг 1: создать наивный индекс

Мы создаем объект, который для каждого уникального элемента во вложенных массивах отслеживает, какой ему предшествовал или предшествовал:

{
  "X": { prev: Set({}), next: Set({ "D", "H", "B", "K", "Z", "A", "M", "T" })
  "M": { prev: Set({ "X" }), next: Set({ "D", "H", "B" })
  // etc.
}

Я назвал это «наивным», потому что эти наборы содержат информацию только одного уровня.

То есть: они сообщают только об отношениях между элементами, которые были в одном массиве.Они не могут видеть, что M предшествует K, потому что они никогда не были в одном и том же массиве.

Шаг 2: рекурсивное объединение индексов

Здесь я проигнорировал всеБиг-о проблем, которые можно иметь ?.Я рекурсивно объединяю индекс: next из M является объединением next из D, H, B.Повторяйте, пока не найдете элемент, который не имеет next, то есть T или A.

Шаг 3: создайте сортировщик, который учитывает индекс сортировки:

const indexSorter = idx => (a, b) => 
    idx[a].next.has(b) || idx[b].prev.has(a) ? -1 :
    idx[a].prev.has(b) || idx[b].next.has(a) ?  1 :
                                                0 ;

Эта функция создает метод сортировки, который использует сгенерированный индекс для поиска порядка сортировки между любыми двумя элементами.

Объединение всего этого вместе:

(function() {


  const naiveSortIndex = xss => xss
    .map(xs =>
      // [ prev, cur, next ]
      xs.map((x, i, xs) => [
        xs.slice(0, i), x, xs.slice(i + 1)
      ])
    )

    // flatten
    .reduce((xs, ys) => xs.concat(ys), [])

    // add to index
    .reduce(
      (idx, [prev, cur, next]) => {
        if (!idx[cur])
          idx[cur] = {
            prev: new Set(),
            next: new Set()
          };

        prev.forEach(p => {
          idx[cur].prev.add(p);
        });

        next.forEach(n => {
          idx[cur].next.add(n);
        });

        return idx;
      }, {}
    );

  const expensiveSortIndex = xss => {
    const naive = naiveSortIndex(xss);

    return Object
      .keys(naive)
      .reduce(
        (idx, k) => Object.assign(idx, {
          [k]: {
            prev: mergeDir("prev", naive, k),
            next: mergeDir("next", naive, k)
          }
        }), {}
      )
  }

  const mergeDir = (dir, idx, k, s = new Set()) =>
    idx[k][dir].size === 0 
      ? s 
      : Array.from(idx[k][dir])
          .reduce(
            (s, k2) => mergeDir(dir, idx, k2, s),
            new Set([...s, ...idx[k][dir]])
          );

  // Generate a recursive sort method based on an index of { key: { prev, next } }
  const indexSorter = idx => (a, b) =>
    idx[a].next.has(b) || idx[b].prev.has(a) ? -1 :
    idx[a].prev.has(b) || idx[b].next.has(a) ? 1 :
    0;

  const uniques = xs => Array.from(new Set(xs));


  // App:
  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 sortIndex = expensiveSortIndex(arrays);
  const sorter = indexSorter(sortIndex);

  console.log(JSON.stringify(
    uniques(arrays.flat()).sort(sorter)
  ))

}())

Рекомендации

Полагаю, элегантное решение проблемы могло бы пропустить все объединения Set с помощью связанного списка / дерева-подобная структура и внедрение элементов в правильные индексы путем обхода, пока не будет найден элемент его prev / next.

0 голосов
/ 11 декабря 2018

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(','));
0 голосов
/ 11 декабря 2018
  1. объединить [].concat.apply([], arrays)
  2. найти uniq [...new Set(merged)]
  3. сортировать .sort()

const arrays = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D']
];


let merged = [].concat.apply([], arrays);  // merge array

let sort = [...new Set(merged)].sort(); // find uniq then sort

console.log(sort);
0 голосов
/ 11 декабря 2018

К вашему коду после слияния нужно удалить дубликаты.Таким образом, вы получите уникальный массив.

Используйте array.sort , чтобы отсортировать массив.

Я надеюсь, что это решит проблему.

const arrays = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D']
]

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

const unique = Array.from(new Set(merged))


const sorted = unique.sort()

console.log("sorted Array", sorted)

// Single Line
      const result = [...new Set([].concat(...arrays))].sort();
      
 console.log("sorted Array single line", result)
0 голосов
/ 11 декабря 2018

Я бы просто сгладил массивы, отобразил их как ключи к объекту (удалив таким образом двойники), а затем отсортировал бы окончательный результат

const arrays = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D']
];

const final = Object.keys( arrays.flat().reduce( (aggregate, entry) => {
  aggregate[entry] = '';
  return aggregate;
}, {} ) ).sort( (x1, x2) => x1.localeCompare(x2) );

console.log( final );
0 голосов
/ 11 декабря 2018

Создайте отдельный массив с помощью array#concat, а затем с помощью Set получите уникальные значения из этого массива, а затем отсортируйте массив.

const arrays = [ ['A', 'B', 'C', 'D'], ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'], ['A', 'A-bis', 'B', 'C', 'D'], ['A', 'C', 'E'], ['A', 'B', 'C', 'D'] ],
      result = [...new Set([].concat(...arrays))].sort();
console.log(result);
0 голосов
/ 11 декабря 2018

Используйте BST для этого.Добавьте все элементы в bst и затем пройдитесь по порядку.

function BST(){
  this.key = null;
  this.value = null;
  this.left = null;
  this.right = null;

  this.add = function(key}{
   const val = key;

   key = someOrderFunction(key.replace(/\s/,''));
   if(this.key == null) {
      this.key = key;
      this.val = val;

   } else if(key < this.key) {
      if(this.left){
        this.left.add(val);
      } else {
        this.left = new BST();
        this.left.key = key;
        this.left.val = val;
      }
   } else if(key > this.key) {

      if(this.right){
        this.right.add(val);
      } else {
        this.right= new BST();
        this.right.key = key;
        this.right.val = val;
      }
   }

   this.inOrder = function(){
      const leftNodeOrder = this.left ? this.left.inOrder() : [],
            rightNodeOrder = this.right? this.right.inOrder() : [];
      return leftNodeOrder.concat(this.val).concat(this.rightNodeOrder);

   }

}

// MergeArrays uses a BST to insert all elements of all arrays
// and then fetches them sorted in order
function mergeArrays(arrays) {
    const bst = new BST();
    arrays.forEach(array => array.forEach( e => bst.add(e)));
    return bst.inOrder();
}
0 голосов
/ 11 декабря 2018

Свести, удалить дубликаты и отсортировать может быть проще:

const arrays = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D'],
];
console.log(
  arrays
    .flat()
    .filter((u, i, all) => all.indexOf(u) === i)
    .sort((a, b) => a.localeCompare(b)),
);

Или более простое событие, согласно теперь удаленному сообщению Мухаммеда Усмана:

const arrays = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D'],
];
console.log(
  [...new Set([].concat(...arrays))].sort((a, b) =>
    a.localeCompare(b),
  ),
);
0 голосов
/ 11 декабря 2018

Вы можете использовать .concat() с Set, чтобы получить результирующий массив уникальных значений:

const data = [
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'B-bis', 'B-ter', 'C', 'D', 'D-bis'],
  ['A', 'A-bis', 'B', 'C', 'D'],
  ['A', 'C', 'E'],
  ['A', 'B', 'C', 'D']
];

const result = [...new Set([].concat(...data))].sort((a, b) => a.localeCompare(b));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
...