Xóa các phần tử bằng Array.map trong JavaScript


90

Tôi muốn lọc một mảng các mục bằng cách sử dụng map()hàm. Đây là một đoạn mã:

var filteredItems = items.map(function(item)
{
    if( ...some condition... )
    {
        return item;
    }
});

Vấn đề là các mục đã lọc ra vẫn sử dụng không gian trong mảng và tôi muốn xóa hoàn toàn chúng.

Bất kỳ ý tưởng?

EDIT: Cảm ơn, tôi quên mất filter(), những gì tôi muốn thực sự là một filter()sau đó a map().

EDIT2: Cảm ơn bạn đã chỉ ra điều đó map()filter()không được triển khai trong tất cả các trình duyệt, mặc dù mã cụ thể của tôi không nhằm mục đích chạy trong trình duyệt.


Bạn có thể giải thích rõ tại sao 2 lần lặp lại kém nhất mà 1 lần lặp lại không? Ý tôi là, 2 * O (n) tương đương với O (2 * n) với tôi ...
Vincent Robert

Câu trả lời:


105

Bạn nên sử dụng filterphương pháp thay vì ánh xạ trừ khi bạn muốn thay đổi các mục trong mảng, ngoài việc lọc.

ví dụ.

var filteredItems = items.filter(function(item)
{
    return ...some condition...;
});

[Chỉnh sửa: Tất nhiên bạn luôn có thể làm sourceArray.filter(...).map(...)để cả bộ lọc và biến đổi]


3
mapkhông đột biến
Cảm ơn bạn

14
Nhưng bạn có thể đột biến map.
Crazywako

Hãy cẩn thận với điều này: vì JS chuyển tham chiếu khi bạn thay đổi thứ gì đó với bản đồ, nó sẽ thay đổi đối tượng nhưng khi MDN là viết tắt, bản đồ trả về mảng đã biến đổi.
alexOtano

1
Câu hỏi không hỏi làm thế nào để lọc, câu hỏi hỏi làm thế nào để xóa trên bản đồ
Dazzle

1
@alexOtano Không, bản đồ không biến đổi và không trả về mảng đã biến đổi. Nó trả về một mảng mới. ví dụ,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
Kyle Baker

40

Lấy cảm hứng từ việc viết câu trả lời này, cuối cùng tôi đã mở rộng và viết một bài blog về vấn đề này một cách chi tiết. Tôi khuyên bạn nên kiểm tra điều đó nếu bạn muốn hiểu sâu hơn về cách suy nghĩ về vấn đề này - Tôi cố gắng giải thích từng phần một và cũng đưa ra so sánh JSperf ở cuối, xem xét tốc độ.

Điều đó nói rằng, tl; dr là thế này: Để thực hiện những gì bạn đang yêu cầu (lọc và ánh xạ trong một lệnh gọi hàm), bạn sẽ sử dụngArray.reduce() .

Tuy nhiên, cách tiếp cận 2 dễ đọc hơn (ít quan trọng hơn) thường nhanh hơn đáng kể là chỉ sử dụng bộ lọc và bản đồ được xâu chuỗi với nhau:

[1,2,3].filter(num => num > 2).map(num => num * 2)

Sau đây là mô tả về cách Array.reduce()hoạt động và cách nó có thể được sử dụng để thực hiện bộ lọc và ánh xạ trong một lần lặp. Một lần nữa, nếu điều này quá cô đọng, tôi thực sự khuyên bạn nên xem bài đăng blog được liên kết ở trên, đây là phần giới thiệu thân thiện hơn nhiều với các ví dụ rõ ràng và tiến trình.


Bạn cung cấp một đối số là một hàm (thường ẩn danh).

Hàm ẩn danh đó nhận hai tham số - một (như các hàm ẩn danh được truyền vào map / filter / forEach) là hàm lặp sẽ được vận hành. Tuy nhiên, có một đối số khác cho hàm ẩn danh được truyền để giảm mà các hàm đó không chấp nhận và đó là giá trị sẽ được truyền giữa các lần gọi hàm, thường được gọi là bản ghi nhớ .

Lưu ý rằng trong khi Array.filter () chỉ nhận một đối số (một hàm), Array.reduce () cũng nhận một đối số thứ hai (mặc dù tùy chọn) quan trọng: một giá trị ban đầu cho 'bản ghi nhớ' sẽ được chuyển vào hàm ẩn danh đó dưới dạng đối số đầu tiên, và sau đó có thể được thay đổi và chuyển giữa các lệnh gọi hàm. (Nếu nó không được cung cấp, thì 'bản ghi nhớ' trong lệnh gọi hàm ẩn danh đầu tiên theo mặc định sẽ là lần lặp đầu tiên và đối số 'lặp lại' sẽ thực sự là giá trị thứ hai trong mảng)

Trong trường hợp của chúng tôi, chúng tôi sẽ truyền vào một mảng trống để bắt đầu, và sau đó chọn có đưa tệp lặp vào mảng của chúng tôi hay không dựa trên chức năng của chúng tôi - đây là quá trình lọc.

Cuối cùng, chúng tôi sẽ trả về 'mảng đang xử lý' của chúng tôi trên mỗi lệnh gọi hàm ẩn danh và giảm sẽ nhận giá trị trả về đó và chuyển nó dưới dạng đối số (được gọi là bản ghi nhớ) cho lệnh gọi hàm tiếp theo của nó.

Điều này cho phép bộ lọc và ánh xạ xảy ra trong một lần lặp, giảm một nửa số lần lặp bắt buộc của chúng tôi - tuy nhiên, chỉ thực hiện gấp đôi công việc mỗi lần lặp, vì vậy không có gì thực sự được lưu ngoài các lệnh gọi hàm, điều này không quá đắt trong javascript .

Để có lời giải thích đầy đủ hơn, hãy tham khảo tài liệu MDN (hoặc bài đăng của tôi được tham chiếu ở đầu câu trả lời này).

Ví dụ cơ bản về lệnh gọi Rút gọn:

let array = [1,2,3];
const initialMemo = [];

array = array.reduce((memo, iteratee) => {
    // if condition is our filter
    if (iteratee > 1) {
        // what happens inside the filter is the map
        memo.push(iteratee * 2); 
    }

    // this return value will be passed in as the 'memo' argument
    // to the next call of this function, and this function will have
    // every element passed into it at some point.
    return memo; 
}, initialMemo)

console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]

phiên bản ngắn gọn hơn:

[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])

Lưu ý rằng vòng lặp đầu tiên không lớn hơn một và vì vậy đã được lọc. Cũng lưu ý đến InitialMemo, được đặt tên chỉ để làm rõ sự tồn tại của nó và thu hút sự chú ý đến nó. Một lần nữa, nó được chuyển dưới dạng 'bản ghi nhớ' cho lệnh gọi hàm ẩn danh đầu tiên, và sau đó giá trị trả về của hàm ẩn danh được chuyển vào dưới dạng đối số 'bản ghi nhớ' cho hàm tiếp theo.

Một ví dụ khác về trường hợp sử dụng cổ điển cho bản ghi nhớ sẽ trả về số nhỏ nhất hoặc lớn nhất trong một mảng. Thí dụ:

[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.

Một ví dụ về cách viết hàm giảm của riêng bạn (điều này thường giúp hiểu các hàm như thế này, tôi tìm thấy):

test_arr = [];

// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
    // if we did not pass in a second argument, then our first memo value 
    // will be whatever is in index zero. (Otherwise, it will 
    // be that second argument.)
    const initialMemoIsIndexZero = arguments.length < 2;

    // here we use that logic to set the memo value accordingly.
    let memo = initialMemoIsIndexZero ? this[0] : initialMemo;

    // here we use that same boolean to decide whether the first
    // value we pass in as iteratee is either the first or second
    // element
    const initialIteratee = initialMemoIsIndexZero ? 1 : 0;

    for (var i = initialIteratee; i < this.length; i++) {
        // memo is either the argument passed in above, or the 
        // first item in the list. initialIteratee is either the
        // first item in the list, or the second item in the list.
           memo = reduceFunc(memo, this[i]);
        // or, more technically complete, give access to base array
        // and index to the reducer as well:
        // memo = reduceFunc(memo, this[i], i, this);
    }

    // after we've compressed the array into a single value,
    // we return it.
    return memo;
}

Việc triển khai thực cho phép truy cập vào những thứ như chỉ mục, chẳng hạn, nhưng tôi hy vọng điều này sẽ giúp bạn có được cảm giác không phức tạp về ý chính của nó.


2
xuất sắc! Tôi đã muốn làm điều gì đó như thế này trong nhiều năm. Đã quyết định thử và tìm ra một javascript hay ho và phá cách và thật tuyệt!
jemiloii 13/09/17

Một tính năng hữu ích khác reducelà, không giống như filter+ map, lệnh gọi lại có thể được truyền một đối số chỉ mục là chỉ mục của mảng ban đầu chứ không phải của mảng được lọc.
congusbongus

@KyleBaker Liên kết đến bài đăng trên blog của bạn chuyển đến một trang không tìm thấy. Bạn có thể vui lòng cập nhật liên kết được không? Cảm ơn!
Tim Philip

10

Đó không phải là những gì bản đồ làm. Bạn thực sự muốn Array.filter . Hoặc nếu bạn thực sự muốn xóa các phần tử khỏi danh sách ban đầu, bạn sẽ cần thực hiện điều đó theo thứ bậc với vòng lặp for.


6

Phương pháp lọc mảng

var arr = [1, 2, 3]

// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })

// ES2015 syntax
arr = arr.filter(item => item != 3)

console.log( arr )


1
bạn cũng có thể làmvar arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
jack trống

Bạn quay lại 4 năm sau để thêm văn bản lớn? trừ một
Cảm ơn bạn

@ user633183 Bạn đang giới thiệu ai? "văn bản khổng lồ" là gì? Nhận xét của bạn là không rõ ràng. Bạn có chắc mình đang bình luận đúng chỗ ...?
vsync

2

Tuy nhiên, bạn phải lưu ý rằng Array.filternó không được hỗ trợ trong tất cả các trình duyệt, do đó bạn phải tạo nguyên mẫu:

//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

if (!Array.prototype.filter)
{
    Array.prototype.filter = function(fun /*, thisp*/)
    {
        var len = this.length;

        if (typeof fun != "function")
            throw new TypeError();

        var res = new Array();
        var thisp = arguments[1];

        for (var i = 0; i < len; i++)
        {
            if (i in this)
            {
                var val = this[i]; // in case fun mutates this

                if (fun.call(thisp, val, i, this))
                   res.push(val);
            }
        }

        return res;
    };
}

Và làm như vậy, bạn có thể tạo nguyên mẫu bất kỳ phương pháp nào bạn có thể cần.


2
Nếu bạn thực sự có ý định polyfill theo phương pháp này, vui lòng sử dụng polyfill thích hợp hoặc tốt hơn là một thư viện như Modernizr . Nếu không, bạn có thể gặp phải lỗi khó hiểu với các trình duyệt khó hiểu mà bạn sẽ không nhận ra cho đến khi chúng được sản xuất quá lâu.
Kyle Baker

0

câu lệnh sau làm sạch đối tượng bằng cách sử dụng hàm bản đồ.

var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);

0

Tôi vừa viết giao điểm mảng xử lý chính xác các bản sao

https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0

// array intersection that correctly handles also duplicates

const intersection = (a1, a2) => {
  const cnt = new Map();
  a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
  return a1.filter(el => el in cnt && 0 < cnt[el]--);
};

const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]


0

Đầu tiên bạn có thể sử dụng bản đồ và với chuỗi bạn có thể sử dụng bộ lọc

state.map(item => {
            if(item.id === action.item.id){   
                    return {
                        id : action.item.id,
                        name : item.name,
                        price: item.price,
                        quantity : item.quantity-1
                    }

            }else{
                return item;
            }
        }).filter(item => {
            if(item.quantity <= 0){
                return false;
            }else{
                return true;
            }
        });
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.