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.