JavaScript에서 LINQ SelectMany ()와 동등한 작업을 수행하는 방법


90

불행히도 JQuery 나 Underscore는없고 순수한 자바 스크립트 (IE9 호환) 만 있습니다.

LINQ 기능에서 SelectMany ()에 해당하는 것을 원합니다.

// SelectMany flattens it to just a list of phone numbers.
IEnumerable<PhoneNumber> phoneNumbers = people.SelectMany(p => p.PhoneNumbers);

할 수 있습니까?

편집하다:

답변 덕분에 다음과 같이 작동했습니다.

var petOwners = 
[
    {
        Name: "Higa, Sidney", Pets: ["Scruffy", "Sam"]
    },
    {
        Name: "Ashkenazi, Ronen", Pets: ["Walker", "Sugar"]
    },
    {
        Name: "Price, Vernette", Pets: ["Scratches", "Diesel"]
    },
];

function property(key){return function(x){return x[key];}}
function flatten(a,b){return a.concat(b);}

var allPets = petOwners.map(property("Pets")).reduce(flatten,[]);

console.log(petOwners[0].Pets[0]);
console.log(allPets.length); // 6

var allPets2 = petOwners.map(function(p){ return p.Pets; }).reduce(function(a, b){ return a.concat(b); },[]); // all in one line

console.log(allPets2.length); // 6

5
그것은 전혀 불행한 일이 아닙니다. 순수 JavaScript는 놀랍습니다. 컨텍스트가 없으면 여기서 달성하려는 것을 이해하기가 매우 어렵습니다.
Sterling Archer

3
@SterlingArcher, 대답이 얼마나 구체적인지 확인하십시오. 가능한 답변이 너무 많지 않았고 베스트 답변은 짧고 간결했습니다.
toddmo

답변:


120

간단한 선택을 위해 Array의 축소 기능을 사용할 수 있습니다.
숫자 배열이 있다고 가정 해 보겠습니다.

var arr = [[1,2],[3, 4]];
arr.reduce(function(a, b){ return a.concat(b); });
=>  [1,2,3,4]

var arr = [{ name: "name1", phoneNumbers : [5551111, 5552222]},{ name: "name2",phoneNumbers : [5553333] }];
arr.map(function(p){ return p.phoneNumbers; })
   .reduce(function(a, b){ return a.concat(b); })
=>  [5551111, 5552222, 5553333]

편집 :
es6 flatMap이 Array 프로토 타입에 추가 되었기 때문에. SelectMany의 동의어 flatMap입니다.
이 메서드는 먼저 매핑 함수를 사용하여 각 요소를 매핑 한 다음 결과를 새 배열로 평면화합니다. TypeScript의 단순화 된 서명은 다음과 같습니다.

function flatMap<A, B>(f: (value: A) => B[]): B[]

작업을 수행하기 위해 각 요소를 phoneNumbers에 flatMap하면됩니다.

arr.flatMap(a => a.phoneNumbers);

11
그 마지막은 같이 쓸 수있다arr.reduce(function(a, b){ return a.concat(b.phoneNumbers); }, [])
Timwi

.map () 또는 .concat ()을 사용할 필요가 없습니다. 아래 내 대답을 참조하십시오.
WesleyAC nov.

이것은 원래 배열을 변경하지 않습니까?
Ewan

덜 중요한 지금,하지만 flatMap- IE9 호환 솔루션에 대한 영업 이익의 요구에 부합하지 않습니다 에 대한 브라우저의 compat를flatmap .
ruffin

24

더 간단한 옵션으로 Array.prototype.flatMap () 또는 Array.prototype.flat ()

const data = [
{id: 1, name: 'Dummy Data1', details: [{id: 1, name: 'Dummy Data1 Details'}, {id: 1, name: 'Dummy Data1 Details2'}]},
{id: 1, name: 'Dummy Data2', details: [{id: 2, name: 'Dummy Data2 Details'}, {id: 1, name: 'Dummy Data2 Details2'}]},
{id: 1, name: 'Dummy Data3', details: [{id: 3, name: 'Dummy Data3 Details'}, {id: 1, name: 'Dummy Data3 Details2'}]},
]

const result = data.flatMap(a => a.details); // or data.map(a => a.details).flat(1);
console.log(result)


3
간단하고 간결합니다. 단연 최고의 답변입니다.
Ste Brown

flat ()은 2019 년 12 월 4 일 현재 MDN에 따라 Edge에서 사용할 수 없습니다.
pettys

그러나 새로운 Edge (Chromium 기반)는이를 지원할 것입니다.
Necip Sunmaz

2
Array.prototype.flatMap ()문제인 것처럼 보이 므로 예제를 다음과 같이 단순화 할 수 있습니다.const result = data.flatMap(a => a.details)
Kyle

12

잠시 후 자바 스크립트를 이해하지만 Typescript에서 간단한 Typed SelectMany 메서드를 원합니다.

function selectMany<TIn, TOut>(input: TIn[], selectListFn: (t: TIn) => TOut[]): TOut[] {
  return input.reduce((out, inx) => {
    out.push(...selectListFn(inx));
    return out;
  }, new Array<TOut>());
}

8

배열을 평면화하기 위해 concat 메서드를 사용하면 Sagi가 정확합니다. 그러나이 예제와 유사한 것을 얻으려면 선택 부분 https://msdn.microsoft.com/library/bb534336(v=vs.100).aspx에 대한 맵도 필요합니다.

/* arr is something like this from the example PetOwner[] petOwners = 
                    { new PetOwner { Name="Higa, Sidney", 
                          Pets = new List<string>{ "Scruffy", "Sam" } },
                      new PetOwner { Name="Ashkenazi, Ronen", 
                          Pets = new List<string>{ "Walker", "Sugar" } },
                      new PetOwner { Name="Price, Vernette", 
                          Pets = new List<string>{ "Scratches", "Diesel" } } }; */

function property(key){return function(x){return x[key];}}
function flatten(a,b){return a.concat(b);}

arr.map(property("pets")).reduce(flatten,[])

나는 바이올린을 만들거야; 데이터를 json으로 사용할 수 있습니다. 한 줄의 코드에 대한 답변을 평평하게 만들려고합니다. "개체 계층 구조를 병합하는 방법에 대한 답변을 병합하는 방법"lol
toddmo

데이터를 자유롭게 사용하십시오 ... 매번 새 함수를 작성하지 않고도 속성 이름을 쉽게 선택할 수 있도록 맵 함수를 명시 적으로 추상화했습니다. 그냥 교체 arr와 함께 people"pets"함께"PhoneNumbers"
파비오 Beltramini

평면 버전으로 내 질문을 편집하고 답변에 투표했습니다. 감사.
toddmo 2015

1
도우미 함수는 정리 작업을 수행하지만 ES6에서는 다음을 수행 할 수 있습니다 petOwners.map(owner => owner.Pets).reduce((a, b) => a.concat(b), []);.. 또는 더 간단하게 petOwners.reduce((a, b) => a.concat(b.Pets), []);.
ErikE

3
// you can save this function in a common js file of your project
function selectMany(f){ 
    return function (acc,b) {
        return acc.concat(f(b))
    }
}

var ex1 = [{items:[1,2]},{items:[4,"asda"]}];
var ex2 = [[1,2,3],[4,5]]
var ex3 = []
var ex4 = [{nodes:["1","v"]}]

시작하자

ex1.reduce(selectMany(x=>x.items),[])

=> [1, 2, 4, "asda"]

ex2.reduce(selectMany(x=>x),[])

=> [1, 2, 3, 4, 5]

ex3.reduce(selectMany(x=> "this will not be called" ),[])

=> []

ex4.reduce(selectMany(x=> x.nodes ),[])

=> [ "1", "v"]

참고 : reduce 함수에서 초기 값으로 유효한 배열 (null이 아님)을 사용하십시오.


3

이것을 시도하십시오 (es6 사용) :

 Array.prototype.SelectMany = function (keyGetter) {
 return this.map(x=>keyGetter(x)).reduce((a, b) => a.concat(b)); 
 }

예제 배열 :

 var juices=[
 {key:"apple",data:[1,2,3]},
 {key:"banana",data:[4,5,6]},
 {key:"orange",data:[7,8,9]}
 ]

사용 :

juices.SelectMany(x=>x.data)

3

나는 이것을 할 것입니다 (.concat () 피하기) :

function SelectMany(array) {
    var flatten = function(arr, e) {
        if (e && e.length)
            return e.reduce(flatten, arr);
        else 
            arr.push(e);
        return arr;
    };

    return array.reduce(flatten, []);
}

var nestedArray = [1,2,[3,4,[5,6,7],8],9,10];
console.log(SelectMany(nestedArray)) //[1,2,3,4,5,6,7,8,9,10]

.reduce ()를 사용하지 않으려면 :

function SelectMany(array, arr = []) {
    for (let item of array) {
        if (item && item.length)
            arr = SelectMany(item, arr);
        else
            arr.push(item);
    }
    return arr;
}

.forEach ()를 사용하려면 :

function SelectMany(array, arr = []) {
    array.forEach(e => {
        if (e && e.length)
            arr = SelectMany(e, arr);
        else
            arr.push(e);
    });

    return arr;
}

2
4 년 전에이 질문을했는데 답변에 대한 스택 오버플로 알림이 나타 났고 지금 제가하고있는 일은 js 배열로 어려움을 겪고있는 것이 재미 있다고 생각합니다. 좋은 재귀!
toddmo

누군가가 그것을 보니 기쁩니다! 스레드가 얼마나 오래 진행되고 얼마나 많은 답변을 시도했는지를 감안할 때 내가 쓴 것과 같은 것이 이미 나열되지 않은 것에 놀랐습니다.
WesleyAC 2011

@toddmo 지금 당장 js 배열 작업을하고 있다면 최근에 추가 한 솔루션에 관심이있을 수 있습니다. stackoverflow.com/questions/1960473/… .
WesleyAC 2011

2

여기에 TypeScript의 joel-harkes의 답변을 확장으로 다시 작성하여 모든 배열에서 사용할 수 있습니다. 따라서 말 그대로 somearray.selectMany(c=>c.someprop). Trans-piled, 이것은 javascript입니다.

declare global {
    interface Array<T> {
        selectMany<TIn, TOut>(selectListFn: (t: TIn) => TOut[]): TOut[];
    }
}

Array.prototype.selectMany = function <TIn, TOut>( selectListFn: (t: TIn) => TOut[]): TOut[] {
    return this.reduce((out, inx) => {
        out.push(...selectListFn(inx));
        return out;
    }, new Array<TOut>());
}


export { };

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