JavaScript에서 Array.map으로 요소 제거


90

map()함수 를 사용하여 항목 배열을 필터링하고 싶습니다 . 다음은 코드 조각입니다.

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

문제는 필터링 된 항목이 여전히 배열의 공간을 사용하므로 완전히 지우고 싶습니다.

어떤 생각?

편집 : 덕분에, 나는 약 잊고 filter(), 내가 원하는 것은 사실이다 filter()다음 map().

EDIT2 : 그 지적에 대한 감사 map()filter()내 특정 코드가 브라우저에서 실행하도록 구성되지 않았지만, 모든 브라우저에서 구현되지 않습니다.


2 회 반복이 1 회보다 최악 인 이유에 대해 자세히 설명해 주시겠습니까? 내 말은, 2 * O (n)은 ... 나에게 O (2 * N)에 해당
빈센트 로버트

답변:


105

filter필터링 외에도 배열의 항목을 변경하려는 경우가 아니라면 매핑보다는 메서드를 사용해야합니다 .

예.

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

[편집 : 물론 당신은 항상 sourceArray.filter(...).map(...)필터링과 돌연변이 모두 할 수 있습니다 ]


3
map변하지 않습니다
감사합니다

14
하지만 map.
Crazywako 2017 년

이것에주의하십시오 : JS가 맵으로 무언가를 변경할 때 참조를 전달하면 객체가 변경되지만 MDN이 의미하는 것처럼 맵은 변경된 배열을 반환합니다.
alexOtano

1
질문은 필터링 방법을 묻지 않았고, 질문은지도에서 삭제하는 방법을 물었습니다
Dazzle

1
@alexOtano 아니요, 맵은 변형되지 않으며 변형 된 배열을 반환하지 않습니다. 새로운 배열을 반환합니다. 예를 들어,x=[1,2,3];y = x.map(z => z*2);console.log(x,y);
카일 베이커

40

이 답변을 작성하면서 영감을 받아 나중에 블로그 게시물을 확장하고 작성하여 자세히 설명했습니다. 이 문제에 대해 생각하는 방법에 대해 더 깊이 이해하고 싶다면 그것을 확인하는 것이 좋습니다. 나는 그것을 하나씩 설명하려고 노력하고 마지막에는 속도 고려 사항을 검토하면서 JSperf 비교를 제공합니다.

즉, tl; dr은 다음과 같습니다. 원하는 것을 수행하려면 (한 함수 호출 내에서 필터링 및 매핑)Array.reduce() .

그러나, 더 읽기 (덜 중요한)은 일반적으로 속도가 매우 빠르고 방법은 사용 필터이며 서로 연결하는지도 :

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

다음은 Array.reduce()작동 방식과이를 사용하여 한 번의 반복으로 필터 및 매핑을 수행하는 방법에 대한 설명입니다 . 다시 말하지만, 이것이 너무 요약되어 있다면 위에 링크 된 블로그 게시물을 보는 것이 좋습니다. 이는 명확한 예와 진행 과정이 포함 된 훨씬 더 친숙한 소개입니다.


(일반적으로 익명) 함수 인 인수를 줄입니다.

이 익명 함수 는 두 개의 매개 변수를 사용합니다. 하나 (map / filter / forEach에 전달 된 익명 함수)는 작동 할 반복자입니다. 그러나이를 줄이기 위해 전달 된 익명 함수에 대한 또 다른 인수가 있는데, 이러한 함수는 허용하지 않으며 , 이는 종종 memo 라고하는 함수 호출 사이에 전달 될 값입니다 .

Array.filter ()는 하나의 인수 (함수) 만 사용하지만 Array.reduce ()는 중요한 (선택 사항이지만) 두 번째 인수도 사용합니다. 즉, 해당 익명 함수에 전달 될 '메모'의 초기 값은 첫 번째 인수이며 이후에 함수 호출 사이에 변형 및 전달 될 수 있습니다. (제공되지 않은 경우 첫 번째 익명 함수 호출의 'memo'는 기본적으로 첫 번째 iteratee가되고 'iteratee'인수는 실제로 배열의 두 번째 값이됩니다.)

우리의 경우에는 빈 배열을 전달하여 시작하고, 함수에 따라 반복자를 배열에 삽입할지 여부를 선택합니다. 이것이 필터링 프로세스입니다.

마지막으로 각 익명 함수 호출에 대해 '진행중인 배열'을 반환하고, reduce는 해당 반환 값을 가져와 다음 함수 호출에 인수 (메모라고 함)로 전달합니다.

이렇게하면 필터와 맵이 한 번의 반복에서 발생하여 필요한 반복 횟수를 절반으로 줄일 수 있습니다.하지만 반복 할 때마다 두 배의 작업 만 수행하므로 자바 스크립트에서 그렇게 비용이 많이 들지 않는 함수 호출 외에는 아무것도 저장되지 않습니다. .

더 자세한 설명은 MDN 문서 (또는이 답변의 시작 부분에 언급 된 내 게시물)를 참조하십시오.

Reduce 호출의 기본 예 :

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

더 간결한 버전 :

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

첫 번째 iteratee는 1보다 크지 않으므로 필터링되었습니다. 또한 그 존재를 명확하게하고주의를 끌기 위해 이름이 지정된 initialMemo에 유의하십시오. 다시 한 번, 첫 번째 익명 함수 호출에 'memo'로 전달되고 익명 함수의 반환 된 값은 다음 함수에 'memo'인수로 전달됩니다.

메모에 대한 고전적인 사용 사례의 또 다른 예는 배열에서 가장 작거나 가장 큰 숫자를 반환하는 것입니다. 예:

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

자신의 감소 함수를 작성하는 방법의 예 (이는 종종 다음과 같은 함수를 이해하는 데 도움이 됨) :

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

예를 들어 실제 구현에서는 인덱스와 같은 항목에 액세스 할 수 있지만 이것이 요점에 대한 복잡하지 않은 느낌을 얻는 데 도움이되기를 바랍니다.


2
훌륭한! 나는 이런 일을 수년 동안하고 싶었다. 멋지고 방법과 와우, 자연스러운 자바 스크립트를 알아 내려고 노력하기로 결정했습니다!
jemiloii

의 또 다른 유용성은 + reduce와 달리 콜백에 필터링 된 배열이 아닌 원래 배열의 인덱스 인 인덱스 인수를 전달할 수 있다는 것입니다. filtermap
congusbongus apr

@KyleBaker 블로그 게시물에 대한 링크는 찾을 수없는 페이지로 이동합니다. 링크를 업데이트 해 주시겠습니까? 감사!
팀 필립

10

그것은지도가하는 일이 아닙니다. 정말 Array.filter 원합니다 . 또는 원래 목록에서 요소를 정말로 제거하려면 for 루프를 사용하여 명령 적으로 제거해야합니다.


6

배열 필터 방법

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
당신은 또한 할 수 있습니다var arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
jack blank

4 년 후 거대한 텍스트를 추가하기 위해 돌아 왔나요? 마이너스 1
감사합니다

@ user633183 누구를 추천하고 있습니까? 무슨 "거대한 텍스트"? 귀하의 의견이 명확하지 않습니다. 올바른 장소에 대해 댓글을 남기고 있습니까 ...?
vsync 19

2

그러나이 Array.filter모든 브라우저에서 지원되는 것은 아니므로 프로토 타입을 작성해야합니다.

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

그렇게하면 필요한 모든 메서드를 프로토 타입 할 수 있습니다.


2
이 방법을 정말로 폴리 필하려는 경우 적절한 폴리 필을 사용하거나 Modernizr 과 같은 라이브러리를 사용하십시오 . 그렇지 않으면 너무 오래 프로덕션 상태가 될 때까지 깨닫지 못할 모호한 브라우저로 혼란스러운 버그가 발생할 수 있습니다.
Kyle Baker

0

다음 문은 map 함수를 사용하여 객체를 정리합니다.

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

0

중복도 올바르게 처리하는 배열 교차를 작성했습니다.

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

먼저 맵을 사용할 수 있고 체인으로 필터를 사용할 수 있습니다.

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;
            }
        });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.