Попытка решить симметричную разницу с помощью Javascript

Я пытаюсь найти решение для симметричных с помощью javascript, который выполняет следующие Цели:

  • принимает неопределенное количество массивов в качестве аргументов
  • сохраняет исходный порядок чисел в массивах
  • не удаляет дубликаты чисел в одиночных массивах
  • удаляет дубликаты, встречающиеся в массивах

Таким образом, например, если вход ([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]), решение было бы [1, 1, 6, 5, 4].

Я пытаюсь решить это как вызов, заданный онлайн сообщества кодирования. Точные инструкции задачи состояние,

Создайте функцию, которая принимает два или более массива и возвращает массив от симметричной разности предоставленных массивов.

Математический термин "симметричная разница" относится к элементам в два набора, которые находятся в первом или втором наборе, но не в обоих.

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

Мой вопрос очень близок к тому, который был задан для нахождения симметричных разностей/уникальных элементов в нескольких массивах в javascript. Однако решение не сохраняет исходный порядок чисел и не сохраняет дубликаты уникальных чисел, встречающихся в одиночных массивах.

function sym(args){
    var arr = [];
    var result = [];
    var units;
    var index = {};
    for(var i in arguments){
        units = arguments[i];

    for(var j = 0; j < units.length; j++){
         arr.push(units[j]);
        }
    }

    arr.forEach(function(a){
        if(!index[a]){
            index[a] = 0;
        }
            index[a]++;

    });

       for(var l in index){
           if(index[l] === 1){
               result.push(+l);
           }
       }

    return result;
}
symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]); // => Desired answer: [1, 1, 6. 5. 4]

Ответ 1

Здесь версия, которая использует объект Set для ускорения поиска. Здесь основная логика:

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

Итак, он начинается с первого массива [1, 1, 2, 6]. Поскольку 1 не найден ни в одном из других массивов, каждый из двух первых значений 1 добавляется к результату. Затем 2 находится во втором наборе, поэтому он не добавляется к результату. Тогда 6 не встречается ни в одном из двух других наборов, поэтому он добавляется к результату. Тот же процесс повторяется для второго массива [2, 3, 5], где 2 и 3 находятся в других наборах, но 5 не так 5 добавляется к результату. И, для последнего массива, только 4 не встречается в других наборах. Итак, конечный результат [1,1,6,5,4].

Объекты Set используются для удобства и производительности. Можно было бы использовать .indexOf() для поиска их в каждом массиве, или вы могли бы сделать свой собственный поиск по типу с простым объектом, если бы не хотели полагаться на объект Set. Также существует частичный polyfill для объекта Set, который будет работать здесь в этом ответе.

function symDiff() {
    var sets = [], result = [];
    // make copy of arguments into an array
    var args = Array.prototype.slice.call(arguments, 0);
    // put each array into a set for easy lookup
    args.forEach(function(arr) {
        sets.push(new Set(arr));
    });
    // now see which elements in each array are unique 
    // e.g. not contained in the other sets
    args.forEach(function(array, arrayIndex) {
        // iterate each item in the array
        array.forEach(function(item) {
            var found = false;
            // iterate each set (use a plain for loop so it easier to break)
            for (var setIndex = 0; setIndex < sets.length; setIndex++) {
                // skip the set from our own array
                if (setIndex !== arrayIndex) {
                    if (sets[setIndex].has(item)) {
                        // if the set has this item
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                result.push(item);
            }
        });
    });
    return result;
}

var r = symDiff([1, 1, 2, 6], [2, 3, 5], [2, 3, 4]);
log(r);

function log(x) {
    var d = document.createElement("div");
    d.textContent = JSON.stringify(x);
    document.body.appendChild(d);
}

Ответ 2

Как и во всех проблемах, лучше всего начать писать алгоритм:

Объединяйте версии массивов, где каждый массив фильтруется, чтобы содержать те элементы, в которых нет другого, кроме текущего, массива

Тогда просто напишите это в JS:

function sym() {
  var arrays = [].slice.apply(arguments);

  return [].concat.apply([],               // concatenate
    arrays.map(                            // versions of the arrays
      function(array, i) {                 // where each array
        return array.filter(               // is filtered to contain
          function(elt) {                  // those elements which
            return !arrays.some(           // no array
              function(a, j) {             // 
                return i !== j             // other than the current one
                  && a.indexOf(elt) >= 0   // contains
                ;
              }
            );
          }
        );
      }
    )
  );
}

Некоммерческая версия, написанная более кратко с использованием ES6:

function sym(...arrays) {
  return [].concat(arrays . 
    map((array, i) => array . 
      filter(elt => !arrays . 
        some((a, j) => i !== j && a.indexOf(elt) >= 0))));
}

Ответ 3

Я столкнулся с этим вопросом в своих исследованиях с тем же вопросом кодирования для FCC. Я смог решить его с помощью циклов for и while, но при решении проблем с использованием Array.reduce() возникли проблемы с решением проблем. Узнав тонну о .reduce и других методах массива, я подумал, что поделюсь своими решениями.

Это первый способ, которым я его решил, без использования .reduce.

function sym() {
  var arrays = [].slice.call(arguments);

  function diff(arr1, arr2) {
    var arr = [];

    arr1.forEach(function(v) {
      if ( !~arr2.indexOf(v) && !~arr.indexOf(v) ) {
        arr.push( v );
      }
    });

    arr2.forEach(function(v) {
      if ( !~arr1.indexOf(v) && !~arr.indexOf(v) ) {
        arr.push( v );
      }
    });
    return arr;
  }

  var result = diff(arrays.shift(), arrays.shift());

  while (arrays.length > 0) {
    result = diff(result, arrays.shift());
  }

  return result;
}

Изучив и пробовав различные комбинации методов, я придумал это, что я считаю довольно кратким и удобочитаемым.

function sym() {
  var arrays = [].slice.call(arguments);

  function diff(arr1, arr2) {
    return arr1.filter(function (v) {
      return !~arr2.indexOf(v);
    });
  }

  return arrays.reduce(function (accArr, curArr) { 
    return [].concat( diff(accArr, curArr), diff(curArr, accArr) )
    .filter(function (v, i, self) { return self.indexOf(v) === i; });
  });

}

Эта последняя строка .filter, которую я считал довольно классной для дедуплирования массива. Я нашел его здесь, но изменил его, чтобы использовать третий параметр обратного вызова вместо именованного массива из-за цепочки методов.

Эта задача была очень интересной!

Ответ 4

Просто используйте _. xor или скопируйте код lodash.

Ответ 5

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

    function sym(args) {
      var output;
      output = [].slice.apply(arguments).reduce(function(previous, current) {
        current.filter(function(value, index, self) { //for unique
          return self.indexOf(value) === index;
        }).map(function(element) { //pushing array
          var loc = previous.indexOf(element);
          a = [loc !== -1 ? previous.splice(loc, 1) : previous.push(element)];
        });
        return previous;
      }, []);
      document.write(output);
      return output;
    }

    sym([1, 2, 3], [5, 2, 1, 4]);

Ответ 6

Чистое решение для javascript.

function diff(arr1, arr2) {
var arr3= [];
  for(var i = 0; i < arr1.length; i++ ){
    var unique = true;
     for(var j=0; j < arr2.length; j++){
          if(arr1[i] == arr2[j]){
               unique = false;
               break;
          }
     }
  if(unique){
    arr3.push(arr1[i]);}
  }
 return arr3;
}

function symDiff(arr1, arr2){
  return diff(arr1,arr2).concat(diff(arr2,arr1));
}

symDiff([1, "calf", 3, "piglet"], [7, "filly"])
//[1, "calf", 3, "piglet", 7, "filly"]

Ответ 7

Мое короткое решение. В конце я удалил дубликаты с помощью filter().

function sym() {
  var args = Array.prototype.slice.call(arguments);
  var almost = args.reduce(function(a,b){
    return b.filter(function(i) {return a.indexOf(i) < 0;})
    .concat(a.filter(function(i){return b.indexOf(i)<0;}));
  });
  return almost.filter(function(el, pos){return almost.indexOf(el) == pos;});
}

sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]);

//Result: [4,5,1]

Ответ 8

Еще одно простое, но удобочитаемое решение:

 
/*
This filters arr1 and arr2 from elements which are in both arrays
and returns concatenated results from filtering.
*/
function symDiffArray(arr1, arr2) {
  return arr1.filter(elem => !arr2.includes(elem))
             .concat(arr2.filter(elem => !arr1.includes(elem)));
}

/*
Add and use this if you want to filter more than two arrays at a time.
*/
function symDiffArrays(...arrays) {
  return arrays.reduce(symDiffArray, []);
}

console.log(symDiffArray([1, 3], ['Saluton', 3])); // [1, 'Saluton']
console.log(symDiffArrays([1, 3], [2, 3], [2, 8, 5])); // [1, 8, 5]

Ответ 9

Это работает для меня:

function sym() {
  var args = [].slice.call(arguments);
  
  var getSym = function(arr1, arr2) {
    return arr1.filter(function(each, idx) {
      return arr2.indexOf(each) === -1 && arr1.indexOf(each, idx + 1) === -1;
    }).concat(arr2.filter(function(each, idx) {
      return arr1.indexOf(each) === -1 && arr2.indexOf(each, idx + 1) === -1;
    }));
  };
  
  var result = getSym(args[0], args[1]);
  var len = args.length - 1, i = 2;
  while (--len) {
    result = [].concat(getSym(result, args[i]));
    i++;
  }
  
  return result;
}

console.info(sym([1, 1, 2, 5], [2, 2, 3, 5], [6, 8], [7, 8], [9]));

Ответ 10

Альтернатива: используйте поиск внутри карты вместо массива

function sym(...vs){
    var has = {};
    //flatten values
    vs.reduce((a,b)=>a.concat(b)).
        //if element does not exist add it (value==1)
        //or mark it as multiply found value > 1
        forEach(value=>{has[value] = (has[value]||0)+1});
    return Object.keys(has).filter(x=>has[x]==1).map(x=>parseInt(x,10));
}
console.log(sym([1, 2, 3], [5, 2, 1, 4],[5,7], [5]));//[3,4,7])

Ответ 11

function sym(args) {
  var initialArray = Array.prototype.slice.call(arguments);
  var combinedTotalArray = initialArray.reduce(symDiff);

  
  // Iterate each element in array,  find values not present in other array and push values in combinedDualArray if value is not there already
  // Repeat for the other array (change roles)
  function symDiff(arrayOne, arrayTwo){
    var combinedDualArray = [];
    arrayOne.forEach(function(el, i){
      if(!arrayTwo.includes(el) && !combinedDualArray.includes(el)){
        combinedDualArray.push(el);
      }
    });
      
    arrayTwo.forEach(function(el, i){
      if(!arrayOne.includes(el) && !combinedDualArray.includes(el)){
        combinedDualArray.push(el);
      }
    });
    combinedDualArray.sort();
    return combinedDualArray;
  }
  
  return combinedTotalArray;
}

console.log(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]));

Ответ 12

Создайте карту с подсчетом всех уникальных значений (по массивам). Затем объедините все массивы и отфильтруйте неуникальные значения, используя карту.

const symsym = (...args) => {
  // create a Map from the unique value of each array
  const m = args.reduce((r, a) => {
    // get unique values of array, and add to Map
    new Set(a).forEach((n) => r.set(n, (r.get(n) || 0) + 1));
    
    return r;
  }, new Map());
  
  // combine all arrays
  return [].concat(...args)
    // remove all items that appear more than once in the map
    .filter((n) => m.get(n) === 1); 
};

console.log(symsym([1, 1, 2, 6], [2, 3, 5], [2, 3, 4])); // => Desired answer: [1, 1, 6, 5, 4]

Ответ 13

Эй, если кто-то заинтересован, это мое решение:

function sym (...args) {
  let fileteredArgs = [];
  let symDiff = [];
  args.map(arrayEl =>
    fileteredArgs.push(arrayEl.filter((el, key) =>
      arrayEl.indexOf(el) === key
      )
    )
  );

  fileteredArgs.map(elArr => {
    elArr.map(el => {
      let index = symDiff.indexOf(el);
      if (index === -1) {
        symDiff.push(el);
      } else {
        symDiff.splice(index, 1);
      }
    });
  });

  return (symDiff);
}

console.log(sym([1, 2, 3, 3], [5, 2, 1, 4]));

Ответ 14

function sym(arr1, arr2, ...rest) {

  //creating a array which has unique numbers from both the arrays
  const union = [...new Set([...arr1,...arr2])];

  // finding the Symmetric Difference between those two arrays
  const diff= union.filter((num)=>!(arr1.includes(num)&&arr2.includes(num)))

  //if there are more than 2 arrays
  if(rest.length){
    // recurrsively call till rest become 0 
    // i.e.  diff of 1,2 will be the first parameter so every recurrsive call will reduce     //  the arrays till diff between all of them are calculated.

    return sym(diff, rest[0], ...rest.slice(1))
  }
  return diff
}

Ответ 15

const exclude = (a, b) => a.filter(v => !b.includes(v))

const symDiff = (first, ...rest) => rest.reduce(
  (acc, x) => exclude(acc, x).concat(exclude(x, acc)), 
  first
)    

console.log(symDiff([1, 3], ['Saluton', 3]))    // [1, 'Saluton']
console.log(symDiff([1, 3], [2, 3], [2, 8, 5])) // [1, 8, 5]