Cách thực hiện tương đương với LINQ SelectMany () chỉ trong javascript


90

Thật không may, tôi không có JQuery hoặc Underscore, chỉ là javascript thuần (tương thích với IE9).

Tôi muốn tương đương với SelectMany () từ chức năng LINQ.

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

Tôi có thể làm được không?

BIÊN TẬP:

Nhờ câu trả lời, tôi đã làm việc này:

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
Điều đó không đáng tiếc chút nào. JavaScript thuần túy thật tuyệt vời. Nếu không có ngữ cảnh, rất khó để hiểu những gì bạn đang cố gắng đạt được ở đây.
Sterling Archer

3
@SterlingArcher, xem câu trả lời hóa ra cụ thể như thế nào. Không có quá nhiều câu trả lời khả thi và câu trả lời tốt nhất là ngắn gọn và súc tích.
toddmo

Câu trả lời:


122

để chọn đơn giản, bạn có thể sử dụng chức năng giảm của Array.
Giả sử bạn có một mảng các dãy số:

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]

Chỉnh sửa:
vì es6 flatMap đã được thêm vào nguyên mẫu Array. SelectManylà từ đồng nghĩa với flatMap.
Đầu tiên, phương pháp này ánh xạ từng phần tử bằng cách sử dụng một hàm ánh xạ, sau đó làm phẳng kết quả thành một mảng mới. Chữ ký đơn giản của nó trong TypeScript là:

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

Để đạt được nhiệm vụ, chúng ta chỉ cần căn hộ từng phần tử thành Số điện thoại

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

11
Đó là người cuối cùng cũng có thể được viết như sauarr.reduce(function(a, b){ return a.concat(b.phoneNumbers); }, [])
Timwi

Bạn không cần phải sử dụng .map () hoặc .concat (). Xem câu trả lời của tôi bên dưới.
WesleyAC

điều này không làm thay đổi mảng ban đầu?
Ewan

Bây giờ ít quan trọng hơn, nhưng flatMapkhông đáp ứng yêu cầu của OP về giải pháp tương thích với IE9 - trình duyệt compat choflatmap .
ruffin

25

Là một tùy chọn đơn giản hơn Array.prototype.flatMap () hoặc 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
Đơn giản và ngắn gọn. Cho đến nay câu trả lời tốt nhất.
Ste Brown

flat () không khả dụng trong Edge theo MDN kể từ ngày 12/4/2019.
nhỏ nhặt

Nhưng Edge mới (dựa trên Chromium) sẽ hỗ trợ nó.
Necip Sunmaz

2
Có vẻ như Array.prototype.flatMap () cũng là một thứ, vì vậy ví dụ của bạn có thể được đơn giản hóa thànhconst result = data.flatMap(a => a.details)
Kyle

12

Đối với những người sau này, hiểu javascript nhưng vẫn muốn có một phương thức Typed SelectMany đơn giản trong Typecript:

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 đã đúng khi sử dụng phương thức concat để làm phẳng một mảng. Nhưng để có được thứ gì đó tương tự như ví dụ này, bạn cũng sẽ cần một bản đồ cho phần được chọn 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,[])

Tôi sẽ làm một trò đùa; Tôi có thể sử dụng dữ liệu của bạn dưới dạng json. Sẽ cố gắng làm phẳng câu trả lời của bạn thành một dòng mã. "Làm thế nào để san bằng một câu trả lời về làm phẳng phân cấp đối tượng" lol
toddmo

Hãy thoải mái sử dụng dữ liệu của bạn ... Tôi đã tóm tắt rõ ràng hàm bản đồ để bạn có thể dễ dàng chọn bất kỳ tên thuộc tính nào mà không cần phải viết hàm mới mỗi lần. Chỉ cần thay thế arrvới people"pets"với"PhoneNumbers"
Fabio Beltramini

Đã chỉnh sửa câu hỏi của tôi với phiên bản phẳng và bình chọn câu trả lời của bạn. Cảm ơn.
toddmo

1
Các chức năng helper làm những việc dọn dẹp, nhưng với ES6 bạn chỉ có thể làm điều này: petOwners.map(owner => owner.Pets).reduce((a, b) => a.concat(b), []);. Hoặc, thậm chí đơn giản hơn 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"]}]

Hãy bắt đầu

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

LƯU Ý: sử dụng mảng hợp lệ (không rỗng) làm giá trị ban đầu trong hàm giảm


3

hãy thử cái này (với es6):

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

mảng ví dụ:

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

sử dụng:

juices.SelectMany(x=>x.data)

3

Tôi sẽ làm điều này (tránh .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]

Nếu bạn không muốn sử dụng .reduce ():

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

Nếu bạn muốn sử dụng .forEach ():

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

    return arr;
}

2
Tôi nghĩ thật buồn cười khi tôi đã hỏi điều này 4 năm trước và thông báo tràn ngăn xếp cho câu trả lời của bạn xuất hiện và những gì tôi đang làm tại thời điểm này là đấu tranh với mảng js. Đệ quy tốt đẹp!
toddmo

Tôi chỉ vui vì ai đó đã nhìn thấy nó! Tôi đã rất ngạc nhiên rằng một cái gì đó như tôi đã viết vẫn chưa được liệt kê, cho dù chủ đề đã diễn ra trong bao lâu và có bao nhiêu câu trả lời đã cố gắng.
WesleyAC

@toddmo Đối với những gì nó đáng giá, nếu bạn đang làm việc trên mảng js ngay bây giờ, bạn có thể quan tâm đến giải pháp tôi đã thêm gần đây ở đây: stackoverflow.com/questions/1960473/… .
WesleyAC

2

Đây là phiên bản được viết lại của câu trả lời joel-harkes trong TypeScript dưới dạng một phần mở rộng, có thể sử dụng trên bất kỳ mảng nào. Vì vậy, bạn có thể sử dụng nó theo nghĩa đen somearray.selectMany(c=>c.someprop). Trans-piled, đây là 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 { };

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.