Как создать набор из массива и удалить исходные элементы в JavaScript
У меня есть массив с повторяющимися значениями.
Я хочу создать набор, чтобы получить различные значения этого массива и удалить или создать новый массив, который будет иметь те же данные минус элементы, необходимые для создания набора.
Это не просто вопрос удаления дубликатов, но удаление одной записи каждого отдельного значения в исходном массиве
Что-то вроде этого работает, но мне интересно, есть ли более прямой подход:
let originalValues = [
'a',
'a',
'a',
'b',
'b',
'c',
'c',
'd'
];
let distinct = new Set(originalValues);
/*
distinct -> { 'a', 'b', 'c', 'd' }
*/
// Perhaps originalValues.extract(distinct) ??
for (let val of distinct.values()) {
const index = originalValues.indexOf(val);
originalValues.splice(index, 1);
}
/*
originalValues -> [
'a',
'a',
'b',
'c'
];
*/
6 ответов:
Вы можете использовать закрытие над a
Set
и проверить на существование.
let originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'], result = originalValues.filter((s => a => s.has(a) || !s.add(a))(new Set)); console.log(result);
Используйте
Array#filter
в сочетании сSet
:
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd']; const remainingValues = originalValues.filter(function(val) { if (this.has(val)) { // if the Set has the value this.delete(val); // remove it from the Set return false; // filter it out } return true; }, new Set(originalValues)); console.log(remainingValues);
Вы не должны использовать
indexOf
внутри цикла, потому что он имеет линейную стоимость, а общая стоимость становится квадратичной. Я бы использовал карту, чтобы подсчитать вхождения каждого элемента в массиве, а затем преобразовать обратно в массив, вычитая одно вхождение.
let originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd']; let freq = new Map(); // frequency table for (let item of originalValues) if (freq.has(item)) freq.set(item, freq.get(item)+1); else freq.set(item, 1); var arr = []; for (let [item,count] of freq) for (let i=1; i<count; ++i) arr.push(item); console.log(arr);
Если все элементы являются строками, вы можете использовать простой объект вместо карты.
Вы можете создать простой цикл
Array.prototype.reduce
сhash table
для подсчета количества вхождений и заполнения результата только в том случае, если это происходит более одного раза.Смотрите демонстрацию ниже:
var originalValues=['a','a','a','a','b','b','b','c','c','d']; var result = originalValues.reduce(function(hash) { return function(p,c) { hash[c] = (hash[c] || 0) + 1; if(hash[c] > 1) p.push(c); return p; }; }(Object.create(null)), []); console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
Вместо использования Set для этого вы можете просто использовать
reduce()
и создать новый массив с уникальными значениями, А также обновить исходный массив с помощьюsplice()
.
let oV = ["a", "a", "a", "a", "b", "b", "c", "c", "d"] var o = {} var distinct = oV.reduce(function(r, e) { if (!o[e]) o[e] = 1 && r.push(e) && oV.splice(oV.indexOf(e), 1) return r; }, []) console.log(distinct) console.log(oV)
В качестве альтернативного подхода можно использовать следующий алгоритм, который удалит только 1-ю запись дублирующего элемента. Если не дублировать, то он ничего не удалит.
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd']; var r = originalValues.reduce(function(p, c, i, a) { var lIndex = a.lastIndexOf(c); var index = a.indexOf(c) if (lIndex === index || index !== i) p.push(c); return p }, []) console.log(r)
Если дубликатов нет, то вы можете напрямую удалить первую итерацию напрямую
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd']; var r = originalValues.filter(function(el, i) { return originalValues.indexOf(el) !== i }) console.log(r)