Xóa nhiều phần tử khỏi mảng trong Javascript / jQuery


117

Tôi có hai mảng. Mảng đầu tiên chứa một số giá trị trong khi mảng thứ hai chứa các chỉ số của các giá trị cần được xóa khỏi mảng đầu tiên. Ví dụ:

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

Tôi muốn xóa các giá trị hiện tại tại các chỉ số 0,2,4khỏi valuesArr. Tôi nghĩ rằng splicephương pháp gốc có thể hữu ích nên tôi đã nghĩ ra:

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

Nhưng nó không hoạt động vì sau mỗi lần splice, chỉ số của các giá trị trong valuesArrkhác nhau. Tôi có thể giải quyết vấn đề này bằng cách sử dụng một mảng tạm thời và sao chép tất cả các giá trị sang mảng thứ hai, nhưng tôi tự hỏi liệu có bất kỳ phương thức gốc nào mà chúng ta có thể chuyển nhiều chỉ số để xóa giá trị khỏi một mảng hay không.

Tôi thích một giải pháp jQuery hơn. (Không chắc tôi có thể sử dụng grepở đây không)

Câu trả lời:


256

Luôn luôn có forvòng lặp cũ đơn giản :

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);

Thực hiện removeValFromIndextheo thứ tự ngược lại và bạn có thể .splice()mà không làm rối chỉ mục của các mục chưa được loại bỏ.

Lưu ý ở phần trên, tôi đã sử dụng cú pháp mảng-chữ với dấu ngoặc vuông để khai báo hai mảng. Đây là cú pháp được khuyến nghị bởi vì new Array()việc sử dụng có thể gây nhầm lẫn do nó phản hồi khác nhau tùy thuộc vào số lượng tham số bạn truyền vào.

CHỈNH SỬA : Vừa thấy nhận xét của bạn về một câu trả lời khác về mảng chỉ mục không nhất thiết phải theo bất kỳ thứ tự cụ thể nào. Nếu đúng như vậy, chỉ cần sắp xếp nó theo thứ tự giảm dần trước khi bạn bắt đầu:

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

Và làm theo điều đó với bất kỳ $.each()phương thức lặp / / vv nào bạn thích.


1
sẽ không cắt mess lên index
Muhammad Umer

5
@MuhammadUmer - Không, không phải nếu bạn làm đúng, đó là những gì câu trả lời của tôi giải thích.
nnnnnn

5
Cảm ơn cho nhận thức về thứ tự ngược lại.
Daniel Nalbach

2
+1, tôi không nhận ra tôi đã phải làm các mối nối theo thứ tự ngược lại, mặc dù thay vì sử dụng foreach, cách tiếp cận của tôi đang sử dụng$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel

1
điều này sẽ chỉ làm việc nếu removeValFromIndex là trong sắp xếp theo thứ tự tăng dần
Kunal Burangi

24

Đây là một cái mà tôi sử dụng khi không đi kèm với lodash / underscore:

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

Giải pháp thanh lịch! Lúc đầu, tôi nghĩ rằng điều này sẽ không hiệu quả vì tôi nghĩ rằng mỗi lần bạn gọi slicebạn sẽ phải tính toán lại các chỉ mục cần loại bỏ (-1 bật IndexestoBeRemoved), nhưng nó thực sự hoạt động!
Renato Gama

2
Quá nhiều thông minh
Farzad YZ

11
Giải pháp này có hiệu quả nếu chỉ có IndexesToBeRemovedmảng được sắp xếp tăng dần.
xfg

Các chỉ số đó sẽ bị vô hiệu sau lần ghép đầu tiên.
shinzou

@shinzou - Không nếu IndexesToBeRemovedđược sắp xếp (tăng dần).
nnnnnn

18

Không in-placenhưng có thể được thực hiện bằng cách sử dụng grepvà các inArraychức năng của jQuery.

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

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

kiểm tra này fiddle.


Nó sẽ (đủ gần) đúng vị trí nếu bạn vừa nóivaluesArr = $.grep(...);
nnnnnn

1
@nnnnnn ha ha ha Tôi sắp đi họp (bạn biết họ làm cho bạn rất hiệu quả) nên không thử nghiệm nhiều.
TheVillageIdiot

1
@ cept0 Tại sao lại là downvote? OP đã yêu cầu một giải pháp jQuery.
Ste77

Cảm ơn bạn rất nhiều! @TheVillageIdiot
ecorvo

jsfiddler - Lỗi 404. Chúng tôi thực sự xin lỗi, nhưng không có trang nào như vậy.
Tro

17

Tôi đề nghị bạn sử dụng Array.prototype.filter

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];
valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
})

indexOf bên trong bộ lọc ... không tối ưu
Alvaro Joao

1
ủng hộ. nhanh hơn so với phương pháp nối theo jsperf.com/remove-multiple/1
lionbigcat

Tuyệt quá! Bây giờ tôi có thể ngủ :)
Firmansyah

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

MDN tham khảo ở đây


@ riship89 The fiddle bị lỗi
mate64

@Karna: fiddle không hoạt động
riship89 Ngày

1
Hãy lưu ý rằng tại thời điểm viết bài (tháng 6 năm 2014), Array.prototype.find là một phần của dự thảo ES6 hiện tại và chỉ thực hiện trong hiện tại firefox
Olli K

6

Trong JS thuần túy, bạn có thể lặp qua mảng ngược lại, vì vậy splice()sẽ không làm rối tung các chỉ số của các phần tử tiếp theo trong vòng lặp:

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

Không làm việc yuck không phải là một chức năng, vì vậy giả định nó là chỉ số của chỉ số yếu tố không mong muốn, và đã qua sử dụng yuck [arr [i]]
DavChana

5

Nó cảm thấy cần thiết để đăng một câu trả lời với O(n)thời gian :). Vấn đề với giải pháp nối là do việc triển khai cơ bản của mảng là một mảng theo nghĩa đen , mỗi lần splicegọi sẽ mất O(n)thời gian. Điều này rõ ràng nhất khi chúng tôi thiết lập một ví dụ để khai thác hành vi này:

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)

Thao tác này loại bỏ các phần tử bắt đầu từ giữa đến đầu, do đó mỗi lần xóa buộc công cụ js sao chép n/2các phần tử, chúng ta có(n/2)^2 tổng số các hoạt động sao chép là bậc hai.

Giải pháp mối nối (giả sử isđã được sắp xếp theo thứ tự giảm dần để loại bỏ chi phí chung) diễn ra như sau:

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

Tuy nhiên, không khó để thực hiện giải pháp thời gian tuyến tính, bằng cách xây dựng lại mảng từ đầu, sử dụng mặt nạ để xem chúng ta có sao chép các phần tử hay không (sắp xếp sẽ đẩy điều này đến O(n)log(n)). Sau đây là một triển khai như vậy (không phải masklà đảo ngược boolean cho tốc độ):

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

Tôi đã chạy điều này trên jsperf.com và đối với ngay cả n=100phương pháp ghép nối cũng chậm hơn 90% đầy đủ. Đối với lớn hơn, nsự khác biệt này sẽ lớn hơn nhiều.


5

Quick ES6 một lớp lót:

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))

Mã của bạn sẽ chạy nhanh hơn nếu bạn thực hiện removeValFromIndexmột Set()và sử dụng removeValFromIndex.hasthay vì includes.
Boris

5

Một giải pháp đơn giản và hiệu quả (độ phức tạp tuyến tính) sử dụng bộ lọcĐặt :

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);

Ưu điểm lớn của việc triển khai đó là thao tác hasTra cứu thiết lập ( hàm) mất một thời gian không đổi, nhanh hơn câu trả lời của nevace chẳng hạn.


@MichaelPaccione Vui mừng khi giúp đỡ :)
Alberto Trindade Tavares

3

Điều này hoạt động tốt cho tôi và cũng hoạt động khi xóa khỏi một mảng đối tượng:

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));

Có thể có một cách ngắn hơn hiệu quả hơn để viết điều này nhưng cách này vẫn hoạt động.


2

Một giải pháp đơn giản bằng cách sử dụng ES5. Điều này có vẻ thích hợp hơn cho hầu hết các ứng dụng ngày nay, vì nhiều người không còn muốn dựa vào jQuery, v.v.

Khi các chỉ mục cần xóa được sắp xếp theo thứ tự tăng dần:

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

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

Khi các chỉ mục cần xóa không được sắp xếp:

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

Bạn có thể sửa mã của mình bằng cách thay thế removeValFromIndexbằng removeValFromIndex.reverse(). Nếu mảng đó không được đảm bảo sử dụng thứ tự tăng dần, bạn có thể sử dụng thay thế removeValFromIndex.sort(function(a, b) { return b - a }).


Có vẻ tốt với tôi - jsfiddle.net/mrtsherman/gDcFu/2 . Mặc dù điều này làm cho giả định rằng danh sách loại bỏ là theo thứ tự.
mrtsherman

@minopret: Cảm ơn nhưng nó sẽ chỉ hoạt động nếu các chỉ mục trong removeValFromIndexthứ tự tăng dần.
xyz


1

Nếu bạn đang sử dụng underscore.js , bạn có thể sử dụng _.filter()để giải quyết vấn đề của mình.

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);
                });

Ngoài ra, nếu bạn đang cố gắng xóa các mục bằng danh sách các mục thay vì chỉ mục, bạn có thể chỉ cần sử dụng _.without(), như sau:

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

Bây giờ filteredArrnên["V2", "V4", "V5"]


Làm thế nào chứa được thực hiện trong gạch ... hãy cẩn thận nếu nó tương đương với indexOf bên trong bộ lọc ... không tối ưu ở tất cả ...
Alvaro Joao

1

filter + indexOf (IE9 +):

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

Hoặc với bộ lọc ES6 + tìm (Edge +):

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

indexOf bên trong bộ lọc ... không tối ưu
Alvaro Joao

1

Đây là một cái nhanh.

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]

indexOf bên trong bộ lọc ... không tối ưu
Alvaro Joao

0

Có vẻ như Áp dụng có thể là những gì bạn đang tìm kiếm.
có lẽ một cái gì đó như thế này sẽ hoạt động?

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

Tuy nhiên, .splice()phương pháp không mong đợi một danh sách các yếu tố để loại bỏ, nó là hy vọng một chỉ số duy nhất của phần tử tại đó để bắt đầu loại bỏ theo sau là số nguyên tố để loại bỏ ...
nnnnnn

0

Đối với nhiều mặt hàng hoặc mặt hàng duy nhất:

Tôi đề nghị bạn sử dụng Array.prototype.filter

Đừng bao giờ sử dụng indexOf nếu bạn đã biết chỉ mục !:

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

Làm:

với Hashes ... bằng Array.prototype.map

  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);

Những công việc này. Tuy nhiên, bạn sẽ tạo một mảng mới trong quá trình này. Không chắc bạn có muốn điều đó hay không, nhưng về mặt kỹ thuật, nó sẽ là một mảng chỉ chứa các giá trị bạn muốn.


-1

Bạn có thể thử và sử dụng delete array[index]Thao tác này sẽ không loại bỏ hoàn toàn phần tử mà đặt giá trị thành undefined.


Cảm ơn nhưng tôi muốn xóa các phần tử.
xyz

-1

Bạn có thể tạo một Settừ mảng và sau đó tạo một mảng từ tập hợp.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.