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.