어레이를 동시에 매핑 및 필터링


155

새로운 필터링 된 배열을 생성하기 위해 반복하려는 객체 배열이 있습니다. 또한 매개 변수에 따라 새 배열에서 일부 객체를 필터링해야합니다. 나는 이것을 시도하고있다 :

function renderOptions(options) {
    return options.map(function (option) {
        if (!option.assigned) {
            return (someNewObject);
        }
    });   
}

좋은 접근입니까? 더 좋은 방법이 있습니까? lodash와 같은 라이브러리를 사용할 수 있습니다.


"object.keys"접근법은 어떻습니까? developer.mozilla.org/fr/docs/Web/JavaScript/Reference/…
Julo0sS


3
.reduce().filter(...).map(...)다른 곳에서 제안한 것보다 확실히 빠릅니다 . JSoverf 테스트를 설정하여 stackoverflow.com/a/47877054/2379922
Justin L.

답변:


219

Array.reduce이것을 위해 사용해야 합니다.

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = options.reduce(function(filtered, option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     filtered.push(someNewValue);
  }
  return filtered;
}, []);

document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>


또는 감속기는 다음과 같은 순수한 기능 일 수 있습니다.

var reduced = options.reduce(function(result, option) {
  if (option.assigned) {
    return result.concat({
      name: option.name,
      newProperty: 'Foo'
    });
  }
  return result;
}, []);

2
나를 위해, 첫 번째 인수 filtered는 객체입니다. filtered.push나에게는 정의 되어 있지 않습니다.
Rahmathullah M

4
나는 또한 filtered물건 이되는 데 문제가 있었다 . []reduce 함수 뒤에 빈 배열 ( ) 인 "초기 값"을 전달하지 않았기 때문 입니다. 잘못된 예 var reduced = options.reduce(function(filtered, option) { ... }); 올바른 var reduced = options.reduce(function(filtered, option) { ... }, []);
존 히긴스

여기에는 이유가 없습니다. 반복 할 때마다 배열을 변경 하므로 순수한 기능이 아니기 때문에 reduce잘 사용할 forEach수 있습니다 filtered. 더 읽기 쉽고 컴팩트 한 이벤트입니다.
Marko

1
@Marko 나는 순수한 감속기를 소개했습니다. PTAL.
thefourtheye

pure 지금은 순수합니다. 노력 +1 나는 순수한 기능 프로그래밍 옹호자가 아니라, 그 점을 알 수 없었습니다. :) 그러나 실제로는 주어진 상황에서 매우 편리하기 때문에 그것을 본 후에 당신의 트릭을 사용했습니다. 그러나이 작업을 위해 flatMap 함수를 살펴보고 싶을 수도 있습니다. 응답을 한 후에 표준으로 제공되었다고 생각합니다 (이 때문에 일부 브라우저에서는 지원되지 않을 수 있음). 이와 같이 배열을 연결하면 O (n ^ 2) 태스크가 O (n) 태스크에서 벗어나게되므로 성능이 더 좋아야합니다.
Marko

43

줄이지, 루크!

function renderOptions(options) {
    return options.reduce(function (res, option) {
        if (!option.assigned) {
            res.push(someNewObject);
        }
        return res;
    }, []);   
}

30

2019 년부터 Array.prototype.flatMap 이 좋은 옵션입니다.

options.flatMap(o => o.assigned ? [o.name] : []);

위에 링크 된 MDN 페이지에서 :

flatMap지도 중에 항목을 추가 및 제거 (항목 수 수정)하는 방법으로 사용할 수 있습니다. 즉, 항상 일대일이 아닌 여러 항목을 여러 항목에 매핑 할 수 있습니다 (각 입력 항목을 개별적으로 처리하여). 이런 의미에서 필터의 반대처럼 작동합니다. 항목을 유지하려면 1 요소 배열을, 항목을 추가하려면 여러 요소 배열을, 항목을 제거하려면 0 요소 배열을 리턴하십시오.


5
훌륭한 솔루션! 그러나 독자들은 flatMap최근에 소개되었고 브라우저 지원이 제한 되어 있음을 명심하십시오 . 공식 폴리 필 / 심 ( flatMapflat) 을 사용할 수 있습니다 .
Arel

24

ES6을 사용하면 매우 짧을 수 있습니다.

options.filter(opt => !opt.assigned).map(opt => someNewObject)


25
어떤 필터 리턴이 여전히 메모리를 차지할 수 있는지에 따라 두 개의 루프가 수행됩니다.
hogan

1
@hogan 그러나 이것은 원래의 질의에 대한 정답 (최적의 솔루션이 될 수 없습니다)입니다
vikramvi

1
@vikramvi 감사합니다. 문제는, 우리는 수많은 방법으로 같은 것을 성취 할 수 있고, 나는 최선의 방법을 선호한다는 것입니다.
hogan 2016 년

10

reduceES6 멋진 확산 구문을 갖춘 한 줄 이 여기 있습니다!

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, {name, assigned}) => [...result, ...assigned ? [name] : []], []);

console.log(filtered);


1
정말 멋진 @Maxim! 나는 이것을 찬성했다! 하지만 ... 추가 된 각 요소에 대해, 그것의 모든 요소를 확산하는 result...처럼 filter(assigned).map(name)솔루션
0zkr 오후

좋은데 무슨 일이 일어나고 있는지 깨닫는 데 시간이 걸렸습니다. ...assigned ? [name] : []– 더 읽기 ...(assigned ? [name] : [])
쉬울

5

의견을 말하지만 필요한 평판이 없습니다. Maxim Kuzmin의 효율성을 높이기 위해 아주 좋은 답변을 약간 개선했습니다.

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, { name, assigned }) => assigned ? result.concat(name) : result, []);

console.log(filtered);

설명

반복 할 때마다 전체 결과를 반복해서 퍼 뜨리지 않고 실제로 삽입 할 값이있을 때만 배열에 추가합니다.


3

Array.prototy.filter 자체 사용

function renderOptions(options) {
    return options.filter(function(option){
        return !option.assigned;
    }).map(function (option) {
        return (someNewObject);
    });   
}

2

어떤 시점에서, 사용하기가 쉽지 않은가 forEach

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = []
options.forEach(function(option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     reduced.push(someNewValue);
  }
});

document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>

이 있다면 그러나 그것은 좋은 것 malter()또는 fap()기능 즉, 결합 mapfilter기능. true 또는 false를 반환하는 대신 객체 또는 null / undefined를 반환하는 것을 제외하고는 필터처럼 작동합니다.


당신은 당신의 밈을 확인하고 싶을 수도 있습니다 ... ;-)
Arel

2

다음 포인트로 답변을 최적화했습니다.

  1. 다시 쓰기 if (cond) { stmt; }cond && stmt;
  2. ES6 화살표 기능 사용

하나는 forEach 를 사용하고 다른 하나 는 reduce를 사용하는 두 가지 솔루션을 제시합니다 .

해결 방법 1 : forEach 사용

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
var reduced = []
options.forEach(o => {
  o.assigned && reduced.push( { name: o.name, newProperty: 'Foo' } );
} );
console.log(reduced);

해결 방법 2 : Reduce 사용

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
var reduced = options.reduce((a, o) => {
  o.assigned && a.push( { name: o.name, newProperty: 'Foo' } );
  return a;
}, [ ] );
console.log(reduced);

어떤 솔루션을 선택해야 할지를 결정합니다.


1
왜 지구상에서 사용 cond && stmt;하시겠습니까? 이것은 읽기가 훨씬 어렵고 전혀 이점이 없습니다.
jlh

1

reduce를 사용하면 하나의 Array.prototype 함수에서이 작업을 수행 할 수 있습니다. 배열에서 모든 짝수를 가져옵니다.

var arr = [1,2,3,4,5,6,7,8];

var brr = arr.reduce((c, n) => {
  if (n % 2 !== 0) {
    return c;
  }
  c.push(n);
  return c;
}, []);

document.getElementById('mypre').innerHTML = brr.toString();
<h1>Get all even numbers</h1>
<pre id="mypre"> </pre>

이와 같은 방법을 사용하여 객체에 대해 일반화 할 수 있습니다.

var arr = options.reduce(function(c,n){
  if(somecondition) {return c;}
  c.push(n);
  return c;
}, []);

arr 이제 필터링 된 개체가 포함됩니다.


0

직접 사용하기는 .reduce어려울 수 있으므로 감속기를 생성하는 함수를 만드는 것이 좋습니다.

function mapfilter(mapper) {
  return (acc, val) => {
    const mapped = mapper(val);
    if (mapped !== false)
      acc.push(mapped);
    return acc;
  };
}

다음과 같이 사용하십시오.

const words = "Map and filter an array #javascript #arrays";
const tags = words.split(' ')
  .reduce(mapfilter(word => word.startsWith('#') && word.slice(1)), []);
console.log(tags);  // ['javascript', 'arrays'];
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.