2017-01-08 6 views
5

У меня есть массив с повторяющимися значениями.Как создать набор из массива и удалить исходные элементы в JavaScript

Я хочу создать набор, чтобы получить различные значения этого массива и удалить или создать новый массив, который будет иметь те же данные MINUS, что и элементы, необходимые для создания Set.

Это не просто вопрос удаления дубликатов, но удалить одну запись из с каждого отдельного значения в исходном массиве

Нечто подобное работает, но мне интересно, если есть более прямой подход:

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' 
]; 
*/ 
+1

Если 'originalValues' начинается с' 'а', 'а', 'a'', она должна заканчиваться '' а», 'a'' или просто '' а «'? – Ryan

+0

Он должен просто удалить один, так что '' a ',' a'' –

+0

@LucasRicoy Имеет ли значение вопрос? Какой из них нужно удалить? – Oriol

ответ

6

Использование 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);

+0

@OriDrori Извинения за непонимание. Чтобы очистить мое понимание, OP хочет удалить 1 дубликат. Правильно, поэтому, если у меня есть «a», «a», «a», 10 раз, мы должны вернуть «9». Я прав? – Rajesh

+0

Это правильно :) –

+0

@Rajesh - не беспокойтесь :) –

2

Вы не должны использовать 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);

Если все элементы строки, которые вы можете использовать простой объект вместо карты.

+0

Ваш код пропустил последний элемент 'd' –

+0

@FarooqKhan 'd' появляется только один раз, и одно вхождение удаляется. Так что это ожидается. – Oriol

+0

Я думаю, что не смотря на комментарий, «это об удалении элементов, которые появляются в наборе, а не об удалении дубликатов», происходит один раз, чтобы он не попадал в критерии вообще. –

1

Вы можете создать простую 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;}

+1

Это хороший подход. Мне это очень нравится :) –

1

Вместо использования набора для этого вы можете просто использовать 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

В качестве альтернативного подхода, вы можете использовать следующий алгоритм, который будет удалять только 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)

+1

не передает пользовательский 'this'' 'filter()', если вы его не используете; это только замедляет работу ... также, третий параметр обратного вызова может помешать вам использовать закрытие, позволяя вашему обратному вызову стать универсальным и повторно использоваться. – dandavis

+0

@dandavis Спасибо, что указали это. Скопировал мой исходный код, который использовал '.reduce' и забыл удалить' [] '. Я обновил свой ответ – Rajesh

3

Вы можете использовать Клоса в течение 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);