Javascript / jQuery'deki diziden birden çok öğeyi kaldırın


118

İki dizim var. İlk dizi bazı değerleri içerirken, ikinci dizi ilk diziden çıkarılması gereken değerlerin dizinlerini içerir. Örneğin:

var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

Ben endeksleri değerler mevcut kaldırmak istediğiniz 0,2,4dan valuesArr. Yerel spliceyöntemin yardımcı olabileceğini düşündüm bu yüzden şunu buldum:

$.each(removeValFromIndex,function(index,value){
    valuesArr.splice(value,1);
});

Ancak işe yaramadı çünkü her birinden sonra splicedeğerlerin indeksleri valuesArrfarklıydı. Bu sorunu geçici bir dizi kullanarak ve tüm değerleri ikinci diziye kopyalayarak çözebilirdim, ancak bir diziden değerleri kaldırmak için birden çok indis geçirebileceğimiz herhangi bir yerel yöntem olup olmadığını merak ediyordum.

Bir jQuery çözümünü tercih ederim. ( grepBurada kullanabileceğimden emin değilim )

Yanıtlar:


256

Her zaman düz eski fordöngü vardır:

var valuesArr = ["v1","v2","v3","v4","v5"],
    removeValFromIndex = [0,2,4];    

for (var i = removeValFromIndex.length -1; i >= 0; i--)
   valuesArr.splice(removeValFromIndex[i],1);

removeValFromIndexTers sırada ilerleyin .splice()ve henüz kaldırılmamış öğelerin dizinlerini karıştırmadan yapabilirsiniz .

Yukarıdakilere dikkat edin, iki diziyi bildirmek için köşeli parantezlerle dizi-değişmez sözdizimini kullandım. Bu, önerilen sözdizimidir, çünkü new Array()kaç parametreyi ilettiğinize bağlı olarak farklı yanıtlar verdiği için kullanım potansiyel olarak kafa karıştırıcıdır.

DÜZENLEME : Dizinler dizisi hakkında belirli bir sırada olması gerekmeyen başka bir yanıt hakkındaki yorumunuzu az önce gördüm. Durum buysa, başlamadan önce sadece azalan sıraya göre sıralayın:

removeValFromIndex.sort(function(a,b){ return b - a; });

Ve $.each()bunu beğendiğiniz döngü / / vb. Yöntemlerle takip edin .


1
dizini karıştırmayacağım
Muhammad Umer

5
@MuhammadUmer - Hayır, doğru yaparsan hayır, cevabım da bunu açıklıyor.
nnnnnn

5
Ters sıra bilinci için teşekkürler.
Daniel Nalbach

2
+1, birleştirmeyi ters sırada yapmam gerektiğini fark etmedim, ancak forEach yerine yaklaşımım kullanıyor$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel

1
bu yalnızca removeValFromIndex artan düzende sıralanırsa işe yarar
Kunal Burangi

24

Lodash / alt çizgi ile gitmediğimde kullandığım bir tanesi:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}

Zarif çözüm! İlk başta bunun işe yaramayacağını düşündüm çünkü her aradığınızda slicekaldırılacak dizinleri yeniden hesaplamanız gerektiğini düşündüm (-1 açık IndexestoBeRemoved), ama aslında işe yarıyor!
Renato Gama

2
Çok fazla zeki
Farzad YZ

11
Bu çözüm, yalnızca IndexesToBeRemoveddizi artan şekilde sıralanırsa işe yarar .
xfg

Bu endeksler ilk eklemeden sonra geçersiz kılınacaktır.
shinzou

@shinzou - Sıralıysa değil IndexesToBeRemoved(artan).
nnnnnn

18

Değil in-placeama kullanılarak yapılabilir grepve inArrayfonksiyonlar arasında jQuery.

var arr = $.grep(valuesArr, function(n, i) {
    return $.inArray(i, removeValFromIndex) ==-1;
});

alert(arr);//arr contains V2, V4

bu kemanı kontrol et .


Az öncevaluesArr = $.grep(...);
nnnnnn

1
@nnnnnn ha ha ha Bir toplantıya çıkıyordum (sizi çok üretken kıldığını biliyorsunuz) bu yüzden pek deneyemedim.
TheVillageIdiot

1
@ cept0 Neden olumsuz oy? OP bir jQuery çözümü istedi.
Ste77

Çok teşekkür ederim! @TheVillageIdiot
ecorvo

jsfiddler - Hata 404. Gerçekten üzgünüz ama böyle bir sayfa yok.
Ash


7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

MDN referansı burada


@ riship89 Keman düştü
mate64

@Karna: fiddle is down
riship89

1
Yazma sırasında (Haziran 2014), Array.prototype.find'ın mevcut ES6 taslağının bir parçası olduğunu ve yalnızca mevcut firefox
Olli K

6

Saf JS'de dizide geriye doğru döngü yapabilirsiniz, bu nedenle splice()döngüde sonraki öğelerin dizinlerini bozmazsınız:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}

Çalışmıyor yuck bir işlev değil, bu yüzden istenmeyen öğe dizinlerinin dizini olduğu varsayıldı ve yuck [arr [i]] kullanıldı
DavChana

5

O(n)Zamanla cevap vermek gerekli geliyor :). Ekleme çözümüyle ilgili sorun, dizinin temeldeki gerçeklemesinin tam anlamıyla bir dizi olması nedeniyle , her spliceçağrının zaman almasıdır O(n). Bu, en çok, bu davranıştan yararlanmak için bir örnek oluşturduğumuzda belirgindir:

var n = 100
var xs = []
for(var i=0; i<n;i++)
  xs.push(i)
var is = []
for(var i=n/2-1; i>=0;i--)
  is.push(i)

Bu, ortadan başlayarak öğeleri kaldırır, dolayısıyla her kaldırma js motorunu n/2öğeleri kopyalamaya zorlar, (n/2)^2toplamda ikinci dereceden kopyalama işlemlerimiz vardır.

Ekleme çözümü ( isgenel giderlerden kurtulmak için azalan sırada sıralandığını varsayarsak) şu şekilde olur:

for(var i=0; i<is.length; i++)
  xs.splice(is[i], 1)

Bununla birlikte, diziyi sıfırdan yeniden oluşturarak, öğeleri kopyalayıp kopyalayamadığımızı görmek için bir maske kullanarak (sıralama bunu zorlayacaktır O(n)log(n)) doğrusal bir zaman çözümünü uygulamak zor değildir . Aşağıdakiler böyle bir uygulamadır ( maskhız için tersine çevrilmiş boole değildir ):

var mask = new Array(xs.length)
for(var i=is.length - 1; i>=0; i--)
  mask[is[i]] = true
var offset = 0
for(var i=0; i<xs.length; i++){
  if(mask[i] === undefined){
    xs[offset] = xs[i]
    offset++
  }
}
xs.length = offset

Bunu jsperf.com'da çalıştırdım ve n=100ekleme yöntemi bile % 90 daha yavaş. Daha büyük olanlar için nbu fark çok daha büyük olacaktır.


5

Quick ES6 tek astar:

const valuesArr = new Array("v1","v2","v3","v4","v5");   
const removeValFromIndex = new Array(0,2,4);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => removeValFromIndex.includes(i))

Bunun yerine removeValFromIndexbir yapar Set()ve kullanırsanız kodunuz daha hızlı removeValFromIndex.hasçalışmalıdır includes.
Boris

5

Filtre ve Set kullanan basit ve verimli (doğrusal karmaşıklık) bir çözüm :

const valuesArr = ['v1', 'v2', 'v3', 'v4', 'v5'];   
const removeValFromIndex = [0, 2, 4];

const indexSet = new Set(removeValFromIndex);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => !indexSet.has(i));

console.log(arrayWithValuesRemoved);

Bu uygulamanın en büyük avantajı, arama işleminin ( hasişlevinin) sabit bir süre alması, örneğin Nevace'ın cevabından daha hızlı olmasıdır.


@MichaelPaccione Yardımcı olmaktan mutluluk duyuyorum :)
Alberto Trindade Tavares

3

Bu benim için iyi çalışıyor ve bir dizi nesneden silerken de işe yarıyor:

var array = [ 
    { id: 1, name: 'bob', faveColor: 'blue' }, 
    { id: 2, name: 'jane', faveColor: 'red' }, 
    { id: 3, name: 'sam', faveColor: 'blue' }
];

// remove people that like blue

array.filter(x => x.faveColor === 'blue').forEach(x => array.splice(array.indexOf(x), 1));

Bunu yazmanın daha kısa ve daha verimli bir yolu olabilir ama bu işe yarıyor.


2

ES5 kullanarak basit bir çözüm. Bu, günümüzde çoğu uygulama için daha uygun görünüyor, çünkü çoğu kişi artık jQuery'ye vb. Güvenmek istemiyor.

Kaldırılacak dizinler artan sırada sıralandığında:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [0, 2, 4]; // ascending

removeValFromIndex.reverse().forEach(function(index) {
  valuesArr.splice(index, 1);
});

Kaldırılacak dizinler sıralanmadığında:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [2, 4, 0];  // unsorted

removeValFromIndex.sort(function(a, b) { return b - a; }).forEach(function(index) {
  valuesArr.splice(index, 1);
});

1

Sen değiştirerek kodunuzu düzeltebilirsiniz removeValFromIndexile removeValFromIndex.reverse(). Bu dizinin artan sıra kullanacağı garanti edilmiyorsa, bunun yerine kullanabilirsiniz removeValFromIndex.sort(function(a, b) { return b - a }).


Bana iyi görünüyor - jsfiddle.net/mrtsherman/gDcFu/2 . Her ne kadar bu, kaldırma listesinin doğru olduğunu varsayıyor.
mrtsherman

@minopret: Teşekkürler, ancak yalnızca içindeki dizinler removeValFromIndexartan sıradaysa çalışacaktır .
xyz


1

Eğer kullanıyorsanız underscore.js kullanabileceğiniz _.filter()sorunu çözmek için.

var valuesArr = new Array("v1","v2","v3","v4","v5");
var removeValFromIndex = new Array(0,2,4);
var filteredArr = _.filter(valuesArr, function(item, index){
                  return !_.contains(removeValFromIndex, index);
                });

Ek olarak, öğeleri dizinler yerine bir öğe listesi kullanarak kaldırmaya çalışıyorsanız _.without(), aşağıdaki gibi kullanabilirsiniz :

var valuesArr = new Array("v1","v2","v3","v4","v5");
var filteredArr = _.without(valuesArr, "V1", "V3");

Şimdi filteredArrolmalı["V2", "V4", "V5"]


Alt çizgi uygulanan içerir Nasıl filtrenin indexOf içine denk olsa ... ... hiç optimum değil ... dikkatli olun
Alvaro Joao

1

filter + indexOf (IE9 +):

function removeMany(array, indexes) {
  return array.filter(function(_, idx) {
    return indexes.indexOf(idx) === -1;
  });
}); 

Veya ES6 filtresi + bul (Kenar +) ile:

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}

indexFiltre içinde ... optimum değil
Alvaro Joao

1

İşte bir hızlılık.

function removeFromArray(arr, toRemove){
    return arr.filter(item => toRemove.indexOf(item) === -1)
}

const arr1 = [1, 2, 3, 4, 5, 6, 7]
const arr2 = removeFromArray(arr1, [2, 4, 6]) // [1,3,5,7]

indexFiltre içinde ... optimum değil
Alvaro Joao

0

Sesler gibi uygula aradığınız şey olabilir.
belki bunun gibi bir şey işe yarar?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );

Ancak .splice()yöntem, bir öğe listesinin kaldırılmasını beklemiyor, kaldırmaya başlayacağı öğenin tek bir dizini ve ardından kaldırılacak öğelerin sayısını bekliyor ...
nnnnnn

0

Birden çok öğe veya benzersiz öğe için:

Array.prototype.filter kullanmanızı öneririm

Zaten indeksi biliyorsanız, indexOf'u asla kullanmayın !:

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];

valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
}); // BIG O(N*m) where N is length of valuesArr and m is length removeValFrom

Yapmak:

Hashes ile ... Array.prototype.map kullanarak

  var valuesArr = ["v1","v2","v3","v4","v5"];
  var removeValFrom = {};
  ([0, 2, 4]).map(x=>removeValFrom[x]=1); //bild the hash.
  valuesArr = valuesArr.filter(function(value, index) {
      return removeValFrom[index] == 1;
  }); // BIG O(N) where N is valuesArr;

0
var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

console.log(valuesArr)
let arr2 = [];

for (let i = 0; i < valuesArr.length; i++){
  if (    //could also just imput this below instead of index value
    valuesArr[i] !== valuesArr[0] && // "v1" <--
    valuesArr[i] !== valuesArr[2] && // "v3" <--
    valuesArr[i] !== valuesArr[4]    // "v5" <--
  ){
    arr2.push(valuesArr[i]);
  }
}

console.log(arr2);

Bu çalışıyor. Ancak, bu süreçte yeni bir dizi yaparsınız. Bunu isteyip istemediğinizden emin değilim, ancak teknik olarak yalnızca istediğiniz değerleri içeren bir dizi olurdu.


-1

Deneyebilir ve kullanabilirsiniz delete array[index]Bu, öğeyi tamamen kaldırmaz, bunun yerine değeri olarak ayarlar undefined.


Teşekkürler ama unsurları kaldırmak istiyorum.
xyz

-1

Bir olustursaydiniz Setdiziden sonra kümesinden bir dizi oluşturmak.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.