LINQ SelectMany () 'nin eşdeğerini yalnızca javascript'te nasıl yapabilirsiniz?


93

Ne yazık ki, JQuery veya Underscore'um yok, sadece saf javascript (IE9 uyumlu).

LINQ işlevinden SelectMany () eşdeğerini istiyorum.

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

Bunu yapabilir miyim?

DÜZENLE:

Cevaplar sayesinde, bunu çalıştırdım:

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
Bu hiç de talihsiz değil. Saf JavaScript harika. Bağlam olmadan, burada neyi başarmaya çalıştığınızı anlamak çok zor.
Sterling Archer

3
@SterlingArcher, cevabın ne kadar spesifik olduğunu görün. Çok fazla olası cevap yoktu ve en iyi cevap kısa ve özdü.
Toddmo

Yanıtlar:


128

basit bir seçim için Array'in azaltma işlevini kullanabilirsiniz.
Bir dizi sayı dizisine sahip olduğunuzu varsayalım:

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]

Düzenle:
es6 flatMap, Array prototipine eklendiğinden beri. SelectManyile eşanlamlıdır flatMap.
Yöntem önce her bir öğeyi bir eşleme işlevi kullanarak eşler, ardından sonucu yeni bir dizide düzleştirir. TypeScript'teki basitleştirilmiş imzası:

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

Görevi başarmak için, her bir öğeyi telefon numaralarına düzleştirmemiz yeterlidir.

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

11
Sonuncusu şu şekilde de yazılabilirarr.reduce(function(a, b){ return a.concat(b.phoneNumbers); }, [])
Timwi

.Map () veya .concat () kullanmanız gerekmemelidir. Aşağıdaki cevabıma bakın.
WesleyAC

bu orijinal diziyi değiştirmez mi?
Ewan

Daha az önemli şimdi, ama flatMap- Bir IE9 uyumlu çözüm için OP'ın isteğini uymayan için tarayıcı COMPATflatmap .
2020 21

1
azaltma () boş dizide başarısız olur, bu nedenle bir başlangıç ​​değeri eklemeniz gerekir. stackoverflow.com/questions/23359173/…
halllo

26

Daha basit bir seçenek olarak Array.prototype.flatMap () veya 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
Basit ve öz. Açık ara en iyi cevap.
Ste Brown

flat (), 12/4/2019 itibarıyla MDN'ye göre Edge'de mevcut değildir.
pettys

Ancak yeni Edge (Chromium tabanlı) bunu destekleyecek.
Necip Sunmaz

2
Array.prototype.flatMap () gibi görünür de bir şey, yani örneğiniz şu şekilde basitleştirilebilirconst result = data.flatMap(a => a.details)
Kyle

12

Bir süre sonra olanlar için, javascript'i anlayın, ancak yine de Typescript'te basit bir Typed SelectMany yöntemini isteyin:

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

Sagi, bir diziyi düzleştirmek için concat yöntemini kullanırken doğrudur. Ancak bu örneğe benzer bir şey elde etmek için, https://msdn.microsoft.com/library/bb534336(v=vs.100).aspx seçim bölümü için bir haritaya da ihtiyacınız olacaktır.

/* 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,[])

Ben bir keman yapacağım; Verilerinizi json olarak kullanabilirim. Cevabınızı bir satır koda düzleştirmeye çalışacak. "Nesne hiyerarşilerini düzleştirme hakkında bir yanıt nasıl düzleştirilir" lol
Toddmo

Verilerinizi kullanmaktan çekinmeyin ... Her seferinde yeni bir işlev yazmak zorunda kalmadan herhangi bir özellik adını kolayca seçebilmeniz için harita işlevini açıkça soyutladım. Sadece yerini arrile peopleve "pets"ile"PhoneNumbers"
Fabio Beltramini

Sorumu düzleştirilmiş versiyonla düzenledim ve cevabınızı oyladı. Teşekkürler.
Toddmo

1
Yardımcı işlevler öğeleri temizlemeye, ama ES6 ile sadece bunu yapabilirsiniz: petOwners.map(owner => owner.Pets).reduce((a, b) => a.concat(b), []);. Veya daha da basit 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"]}]

Hadi başlayalım

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

NOT: indirgeme işlevinde başlangıç ​​değeri olarak geçerli dizi (boş olmayan) kullanın


3

bunu deneyin (es6 ile):

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

örnek dizi:

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

kullanma:

juices.SelectMany(x=>x.data)

3

Bunu yapardım (.concat () kullanmaktan kaçınarak):

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 () kullanmak istemiyorsanız:

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

.ForEach () kullanmak istiyorsanız:

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

    return arr;
}

2
Sanırım bunu 4 yıl önce sormuş olmam ve cevabınız için yığın taşması bildirimi ortaya çıktı ve şu anda yaptığım şey js dizileriyle mücadele ediyor. Güzel yineleme!
Toddmo

Birinin görmesine sevindim! Yazdığım gibi bir şeyin, iş parçacığının ne kadar süredir devam ettiği ve kaç tane cevap denenmesi olduğu göz önüne alındığında, zaten listelenmemiş olmasına şaşırdım.
WesleyAC

@toddmo Ne olursa olsun, şu anda js dizileri üzerinde çalışıyorsanız, buraya yakın zamanda eklediğim çözümle ilgilenebilirsiniz: stackoverflow.com/questions/1960473/… .
WesleyAC

2

Joel-harkes'ın yanıtının TypeScript'te herhangi bir dizide kullanılabilen bir uzantı olarak yeniden yazılmış bir versiyonu. Böylece onu tam anlamıyla kullanabilirsiniz somearray.selectMany(c=>c.someprop). Çapraz dizilmiş, bu 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 { };

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.