Между двумя входными массивами найдите один и тот же массив или массивы и верните их.
Я просто мог придумать решение для вложенного цикла.
const compareArrs = (arr1, arr2) => {
const rtnArr = []
for (let ar1 of arr1){
for (let ar2 of arr2){
if (JSON.stringify(ar1) === JSON.stringify(ar2)){
rtnArr.push(ar1)
}
}
}
return rtnArr
}
// compare [1, 2] [3, 4] with [1, 2]
console.info(compareArrs([[1, 2], [3, 4]], [[1, 2]]))
[[1, 2]] должно быть возвращено.
Сопоставьте каждый подмассив одного аргумента и поместите его в набор. С другим аргументом выполните итерацию по нему, составляя строки по мере продвижения, и найдите элемент, для которого строковый массив уже находится в наборе, для общей сложности O(N)
:
const compareArrs = (arr1, arr2) => {
const arr1StringifiedSet = new Set(arr1.map(JSON.stringify));
return arr2.find(
subarr2 => arr1StringifiedSet.has(JSON.stringify(subarr2))
);
};
console.info(compareArrs([[1, 2], [3, 4]], [[1, 2]]));
(Для сравнения, вложенный цикл имеет сложность O(N^2)
)
Как отмечается в комментариях, если вам действительно нужны совпадающие массивы все, а не только первый, используйте .filter
вместо .find
:
const compareArrs = (arr1, arr2) => {
const arr1StringifiedSet = new Set(arr1.map(JSON.stringify));
return arr2.filter(
subarr2 => arr1StringifiedSet.has(JSON.stringify(subarr2))
);
};
console.info(compareArrs([[1, 2], [3, 4]], [[1, 2]]));
Я считаю, что второй аргумент может иметь более одного элемента, иначе для него нет смысла быть массивом. В этом случае вы можете использовать filter()
вместо find()
.
Вопрос говорит find the same array and return them.
, поэтому я понял, что это означает, что будет соответствующий элемент один. Конечно, если он хочет найти совпадающие массивы все, используйте вместо этого filter
Да, может быть, однако, как не носитель английского языка, и беря себя в качестве примера, иногда мы пишем не совсем то, что нам нужно. Просто говорю :)
Извините за путаницу. Как сказал Шидерс, может быть несколько совпадающих массивов. Я только что изменил свой вопрос.
@JinYongKim Похоже, ваш ожидаемый результат на самом деле [[1, 2]]
, а не [1, 2]
, если вы хотите, чтобы результатом был массив всех совпадающих массивов?
Да, вы правы, я снова отредактировал свой вопрос. Спасибо, что дал мне знать.
Вы можете имитировать набор массивов с вложенными картами, где [1, 2] становится записью в корневой карте, где ключ равен 1, а значение — другая карта с ключом 2, например:
const present = Symbol('present');
class ArraySet {
constructor() {
this._root = new Map();
}
add(array) {
let node = this._root;
for (const item of array) {
if (node.has(item)) {
node = node.get(item);
} else {
node.set(item, node = new Map());
}
}
node[present] = true;
}
has(array) {
let node = this._root;
for (const item of array) {
node = node.get(item);
if (node === undefined) {
return false;
}
}
return !!node[present];
}
}
тогда:
const compareArrs = (arr1, arr2) => {
const set = new ArraySet();
arr1.forEach(set.add, set);
return arr2.filter(set.has, set);
};
const present = Symbol('present');
class ArraySet {
constructor() {
this._root = new Map();
}
add(array) {
let node = this._root;
for (const item of array) {
if (node.has(item)) {
node = node.get(item);
} else {
node.set(item, node = new Map());
}
}
node[present] = true;
}
has(array) {
let node = this._root;
for (const item of array) {
node = node.get(item);
if (node === undefined) {
return false;
}
}
return !!node[present];
}
}
const compareArrs = (arr1, arr2) => {
const set = new ArraySet();
arr1.forEach(set.add, set);
return arr2.filter(set.has, set);
};
console.info(compareArrs([[1, 2], [3, 4]], [[1, 2]]));
Это занимает время, пропорциональное arr1.concat(arr2).flat().length
. Другой подход, который работает до тех пор, пока вы можете создать соответствующую функцию сравнения для массивов, например эту лексикографическую, когда обычные операторы JavaScript дают общий порядок:
const lexCompare = (a, b) => {
const len = Math.min(a.length, b.length);
for (let i = 0; i < len; i++) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return a.length - b.length;
};
и входные массивы не содержат дубликатов должен сначала отсортировать комбинацию обоих массивов:
const sorted = arr1.concat(arr2).sort(lexCompare);
затем найдите дубликаты, созданные слиянием, рядом друг с другом:
return sorted.filter((item, i) => {
if (i === 0) return false;
const prev = sorted[i - 1];
return prev.length === item.length && prev.every((x, i) => x === item[i]);
});
за время O((|arr1| + |arr2|) log (|arr1| + |arr2|) l), где l — максимальная длина массива. Вы можете сократить этот (|arr1| + |arr2|) журнал (|arr1| + |arr2|) до |arr1| журнал |обр1| + |обр2| журнал |обр2| но это может оказаться медленнее.
Вы можете использовать Массив.прототип.уменьшить() в сочетании с Массив.прототип.найти() и Массив.прототип.push().
const compareArrs = (arr1, arr2) => arr1.reduce((a, c) => {
const found = arr2.find(arr => JSON.stringify(arr) === JSON.stringify(c))
if (found.length) a.push(found)
return a
}, [])
// compare [1, 2] [3, 4] with [1, 2]
console.info(compareArrs([[1, 2]], [[1, 2], [3, 4]]))
Это на самом деле имеет более медленный худший случай, чем код в вопросе, из-за нажатия с расширением массива.
@Ry- Вы правы, я улучшил свой ответ, используя Array#concat().. Спасибо за комментарий.
[].concat
это тоже самое. Вам нужно использовать push
.
@Ry- Обновленный ответ, большое спасибо за комментарии
Я не думаю, что за этот вопрос следует проголосовать, поскольку он задает вопрос о том, как сделать вышеизложенное лучше.