Я пытаюсь отфильтровать массив на наличие повторяющихся значений, разделив/подстроив элементы.
Мне нужно разделить эти данные на «-», найти повторяющуюся строку после «-» и записать элемент с повторяющейся строкой в повторяющийся массив.
Вот над чем я работаю:
var arr = ['abc-10.10.10.0/22','abc-10.01.21.0/22','abc-10.01.01.0/22','abcd-10.01.01.0/22'];
var duplicates = [];
arr.forEach(function (value, index, array) {
if (array.indexOf(value, index + 1) !== -1
&& duplicates.indexOf(value) === -1) {
duplicates.push(value);
}
});
console.info("Duplicate values:", duplicates);
//Desired output 'abc-10.01.01.0/22','abcd-10.01.01.0/22'
🤔 А знаете ли вы, что...
JavaScript может выполняться как на стороне клиента (в браузере), так и на стороне сервера (с использованием Node.js).
Самая простая реализация — просто группируйте по подстроке и возвращайте все результаты со счетчиком > 1.
function getDuplicates(xs, selector) {
const group = Object.groupBy(xs, selector);
return Object.keys(group)
.filter(k => group[k].length > 1)
.map(k => group[k])
.flat();
}
var arr = ['abc-10.10.10.0/22','abc-10.01.21.0/22','abc-10.01.01.0/22','abcd-10.01.01.0/22'];
getDuplicates(arr, (x) => x.split('-')[1])
В этом случае вы пытаетесь найти уникальное значение селектора, но сохраняете исходные значения. При этом нам необходимо отслеживать как повторяющиеся подстроки, так и те подстроки, которые связаны с исходным значением.
Эта реализация полностью решает эту проблему, и единственным преимуществом является отсутствие необходимости фильтровать перед выбором значений за счет дополнительного набора. Я не думаю, что это стоит возможного небольшого прироста производительности (код сложнее поддерживать/менее легко понять, увеличивается сложность пространства), но тем не менее я это предоставил.
function getDuplicates(xs, selector) {
const selectSet = new Set(); // list of all duplicate found selectors
const selectToXs = {}; // key of selector to return original values
for(const x of xs) {
const sub = selector(x);
if (sub in selectToXs) {
selectSet.add(sub);
selectToXs[sub].add(x);
} else {
selectToXs[sub] = new Set([x]);
}
}
return [...selectSet].map((s) => [...selectToXs[s]]).flat();
}
var arr = ['abc-10.10.10.0/22','abc-10.01.21.0/22','abc-10.01.01.0/22','abcd-10.01.01.0/22'];
getDuplicates(arr, (x) => x.split('-')[1])
вероятно, не оптимально:
const arr = ['abc-10.10.10.0/22','abc-10.01.21.0/22','abc-10.01.01.0/22','abcd-10.01.01.0/22'];
const afterSet = new Set();
const duplicateSet = new Set();
arr.forEach(function(str){
const afterString = str.split('-').at(1);
if (afterString !== undefined &&
afterSet.size === afterSet.add(afterString).size){
duplicateSet.add(afterString);
}
});
const duplicates = arr.filter(
function(str){
return duplicateSet.has(str.split('-').at(1))
}
);
console.info("Duplicate values:", duplicates);
//Desired output 'abc-10.01.01.0/22','abcd-10.01.01.0/22'
Очень старый способ сделать это — сгруппировать элементы по ключу, а затем свести значения в один массив.
var arr = [
'abc-10.10.10.0/22',
'abc-10.01.21.0/22',
'abc-10.01.01.0/22', // Duplicate
'abcd-10.01.01.0/22' // Duplicate
];
var groups = arr.reduce(function(groups, value, index, array) {
var tokens = value.split('-');
var key = tokens.length > 0 ? tokens[1] : value;
if (groups[key] === undefined) {
groups[key] = [];
}
groups[key].push(value);
return groups;
}, {});
var duplicates = Object.values(groups)
.reduce(function(duplicates, values) {
if (values.length > 1) {
return duplicates.concat(values);
}
return duplicates;
}, []);
// Expected: abc-10.01.01.0/22 abcd-10.01.01.0/22
console.info("Duplicate values:", ...duplicates);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Если вы хотите сделать это в линейном времени, вам понадобится карта и набор для отслеживания увиденного и поставленного соответственно.
const arr = [
'abc-10.10.10.0/22',
'abc-10.01.21.0/22',
'abc-10.01.01.0/22', // Duplicate
'abcd-10.01.01.0/22' // Duplicate
];
const duplicates = findDuplicates(arr, (v) => v.split('-').pop());
// Expected: abc-10.01.01.0/22 abcd-10.01.01.0/22
console.info('Duplicates:', ...duplicates);
function findDuplicates(arr, keyFn) {
const result = [];
const seen = new Set();
const staged = new Map();
for (let item of arr) {
const key = keyFn?.(item) ?? item;
if (!seen.has(key)) {
seen.add(key);
staged.set(key, item);
} else {
if (staged.has(key)) {
result.push(staged.get(key));
staged.delete(key);
}
result.push(item);
}
}
console.info('Unique: ', ...[...staged.values()]); // DEBUG!
return result;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
Чтобы использовать Array#indexOf
, вам сначала нужно будет использовать Array#map
и String#split
для создания нового массива целевых подстрок.
Но вы также можете добавить внутренний цикл и использовать равенство для сравнения строк следующим образом:
const arr = ['abc-10.10.10.0/22','abc-10.01.21.0/22','abc-10.01.01.0/22','abcd-10.01.01.0/22'];
const duplicates = [];
arr.forEach(function (value, index, array) {
array.slice(index+1).forEach(function(val, i) {
if (value.split('-')[1] === val.split('-')[1]
&& duplicates.indexOf(value.split('-')[1]) === -1) {
duplicates.push(value);
}
});
});
console.info("Duplicate values:", duplicates);
//Desired output 'abc-10.01.01.0/22','abcd-10.01.01.0/22'