SelectとSelectManyの違い


1074

私は違いを検索してきたSelectSelectMany私は、適切な答えを見つけることができませんでした。LINQ To SQLを使用する場合の違いを知る必要がありますが、私が見つけたのは標準的な配列の例だけです。

誰かがLINQ To SQLの例を提供できますか?


8
あなたは一つの機能を持つ、または二つの機能を持つSelectManyのコードを見ることができreferencesource.microsoft.com/#System.Core/System/Linq/...
barlop

1
Kotlinに精通している場合、それはコレクションのmap aka C#SelectおよびflatMap aka C#SelectManyと非常によく似た実装です。基本的に、コレクションのKotlin stdライブラリ拡張関数は、C#Linqライブラリと類似しています。
Arsenius

回答:


1622

SelectManyリストのリストを返すクエリをフラット化します。例えば

public class PhoneNumber
{
    public string Number { get; set; }
}

public class Person
{
    public IEnumerable<PhoneNumber> PhoneNumbers { get; set; }
    public string Name { get; set; }
}

IEnumerable<Person> people = new List<Person>();

// Select gets a list of lists of phone numbers
IEnumerable<IEnumerable<PhoneNumber>> phoneLists = people.Select(p => p.PhoneNumbers);

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

// And to include data from the parent in the result: 
// pass an expression to the second parameter (resultSelector) in the overload:
var directory = people
   .SelectMany(p => p.PhoneNumbers,
               (parent, child) => new { parent.Name, child.Number });

.NET Fiddleのライブデモ


1
ネストされた階層構造をフラット化するためのSelectManyのネストに関する質問
赤エンドウ

1
resultSelectorをさらに理解するには以下のリンクがblogs.interknowlogy.com/2008/10/10/…に
jamir

親からの結果を含むもう1つのデモ:dotnetfiddle.net/flcdCC
Evgeniy Kosjakov

フィドルリンクをありがとう!
Aerin

197

多くを選択することは、クロス積をとるSQLのクロス結合操作のようなものです。
たとえば、

Set A={a,b,c}
Set B={x,y}

多くを選択して、次のセットを取得するために使用できます

{ (x,a) , (x,b) , (x,c) , (y,a) , (y,b) , (y,c) }

ここでは、セットAとセットBの要素から作成できるすべての可能な組み合わせを取り上げています。

ここにあなたが試すことができるLINQの例があります

List<string> animals = new List<string>() { "cat", "dog", "donkey" };
List<int> number = new List<int>() { 10, 20 };

var mix = number.SelectMany(num => animals, (n, a) => new { n, a });

ミックスは次のようなフラットな構造の要素になります

{(10,cat), (10,dog), (10,donkey), (20,cat), (20,dog), (20,donkey)}

4
これは古いことはわかっていますが、ありがとうございました。とても助かりました!:)これらのコードへの参照も持つと便利です:stackoverflow.com/questions/3479980/…乾杯!
user3439065 2014年

4
SelectManyはそのように使用する必要はありません。これには、1つの関数のみを使用するオプションもあります。
barlop

2
それは、これがどのようであると言うことは右だ場合、私は知らないSelectMany です。むしろ、これはSelectMany使用できる方法ですが、実際には通常の使用方法ではありません。
Dave Cousineau 2017年

1
これは私が理解する最も簡単な答えでした。
Chaim Eliyah 2017年

WhereSelectMany後の状態も示すとよいでしょう
Nitin Kt

126

ここに画像の説明を入力してください

var players = db.SoccerTeams.Where(c => c.Country == "Spain")
                            .SelectMany(c => c.players);

foreach(var player in players)
{
    Console.WriteLine(player.LastName);
}
  1. デギア
  2. アルバ
  3. コスタ
  4. ヴィラ
  5. バスケ

...


9
優れたサンプルデータ
ben_mj

2
selectの例を追加して、この回答を完了することができます:)
Harry

73

SelectMany()2 Select()次元ループを必要とする方法で多次元シーケンスを折りたたむことができます。

詳細については、このブログ投稿をご覧ください。


しかし、最初の例はEnumerables型の子を返し、2番目の例は戻り値の型の親を返しますか?実は少し戸惑っていますが、もう少し開いていただけますか?
Tarik

逆に、実際に。2番目は、列挙型の階層を完全にフラット化して、Childrenを取り戻すようにします。追加したリンクの記事を試してみて、それが役立つかどうかを確認してください。
マイケル・ペロッタ

最初のものは合法ではないようです。自分でポスターが混乱したと思います。2つ目は、列挙可能な親を返します。
mqp

ありがとう、まあ実際にはそうです。例はちょっとわかりづらかったです:)しかし、私を助けてくれてありがとう。
Tarik

37

いくつかのオーバーロードがあります SelectMany。それらの1つを使用すると、階層をトラバースしながら、親と子の関係を追跡できます。

:次の構造があるとします。League -> Teams -> Player

フラットなプレーヤーのコレクションを簡単に返すことができます。ただし、プレーヤーが所属しているチームへの参照が失われる可能性があります。

幸いなことに、そのような目的のための過負荷があります:

var teamsAndTheirLeagues = 
         from helper in leagues.SelectMany
               ( l => l.Teams
                 , ( league, team ) => new { league, team } )
                      where helper.team.Players.Count > 2 
                           && helper.league.Teams.Count < 10
                           select new 
                                  { LeagueID = helper.league.ID
                                    , Team = helper.team 
                                   };

前の例は、DanのIKブログからの抜粋です。ぜひご覧になってみてください。


19

SelectMany結合ショートカットのように機能することを理解しています。

だからあなたはできる:

var orders = customers
             .Where(c => c.CustomerName == "Acme")
             .SelectMany(c => c.Orders);

提供されている例は機能しますが、SelectManyは結合のようには機能しません。結合により、元のテーブルの任意のフィールドと結合されたテーブルの任意のフィールドを「使用」することができます。ただし、ここでは、元のテーブルに添付されているリストのオブジェクトを指定する必要があります。たとえば、.SelectMany(c => new {c.CompanyName, c.Orders.ShippedDate});動作しません。SelectManyはリストのリストをかなりフラット化しており、結果として含まれるリストのいずれか(一度に1つだけ)を選択できます。比較のために:Linqの内部結合
マット

13

選択は、ソース要素から結果要素への単純な1対1の投影です。Select- Manyは、クエリ式に複数のfrom句がある場合に使用されます。元のシーケンスの各要素を使用して、新しいシーケンスが生成されます。


7

一部のSelectManyは必要ない場合があります。以下の2つのクエリは同じ結果になります。

Customers.Where(c=>c.Name=="Tom").SelectMany(c=>c.Orders)

Orders.Where(o=>o.Customer.Name=="Tom")

1対多の関係の場合、

  1. 「1」から開始する場合、SelectManyが必要で、多くをフラット化します。
  2. 「多数」から開始する場合、SelectManyは必要ありません。("1"からフィルタリングすることもできます。これは標準の結合クエリよりも簡単です)

from o in Orders
join c in Customers on o.CustomerID equals c.ID
where c.Name == "Tom"
select o

4

あまり技術的になることなく-多くの組織があり、それぞれが多くのユーザーを持つデータベース:-

var orgId = "123456789";

var userList1 = db.Organizations
                   .Where(a => a.OrganizationId == orgId)
                   .SelectMany(a => a.Users)
                   .ToList();

var userList2 = db.Users
                   .Where(a => a.OrganizationId == orgId)
                   .ToList();

どちらも選択した組織の同じ ApplicationUserリストを返します。

最初の「プロジェクト」は組織からユーザーへ、2番目はユーザーテーブルを直接クエリします。


3

クエリが文字列(charの配列)を返すと、より明確になります。

たとえば、リスト「フルーツ」に「リンゴ」が含まれている場合

「選択」は文字列を返します。

Fruits.Select(s=>s) 

[0]: "apple"

'SelectMany'は文字列をフラット化します。

Fruits.SelectMany(s=>s)

[0]: 97  'a'
[1]: 112 'p'
[2]: 112 'p'
[3]: 108 'l'
[4]: 101 'e'

2

一部の関数型プログラマーを助けるかもしれない別のビューのためだけに:

  • Select です map
  • SelectManybind(またはflatMapScala / Kotlinの人々にとって)

2

この例を考えてみましょう:

        var array = new string[2]
        {
            "I like what I like",
            "I like what you like"
        };
        //query1 returns two elements sth like this:
        //fisrt element would be array[5]  :[0] = "I" "like" "what" "I" "like"
        //second element would be array[5] :[1] = "I" "like" "what" "you" "like"
        IEnumerable<string[]> query1 = array.Select(s => s.Split(' ')).Distinct();

        //query2 return back flat result sth like this :
        // "I" "like" "what" "you"
        IEnumerable<string> query2 = array.SelectMany(s => s.Split(' ')).Distinct();

つまり、「SelectMany」は複数のシーケンスにわたってフラット化および投影するため、「I」や「like」などの重複する値がquery2から削除されています。しかし、query1は文字列配列のシーケンスを返します。また、query1には2つの異なる配列(最初と2番目の要素)があるため、何も削除されません。


おそらく、最後に.Distinct()を含めて、「I」、「like」、「what」、「I」、「like」、「I」、「like」、「what」、「you」、「like」を出力することをお勧めします
Prof

1

サブ配列オブジェクトデータを蓄積するためにSelectMany + Selectを使用する方法のもう1つの例。

電話を持っているユーザーがいるとします。

class Phone { 
    public string BasePart = "555-xxx-xxx"; 
}

class User { 
    public string Name = "Xxxxx";
    public List<Phone> Phones; 
}

次に、すべてのユーザーのすべての電話のBasePartを選択する必要があります。

var usersArray = new List<User>(); // array of arrays
List<string> allBaseParts = usersArray.SelectMany(ua => ua.Phones).Select(p => p.BasePart).ToList();

あなたはどちらが良いと思いますか?Yours orusersArray.SelectMany(ua => ua.Phones.Select(p => p.BasePart))
Michael Best

-1

以下は、テスト用に初期化された小さなコレクションを含むコード例です。

class Program
{
    static void Main(string[] args)
    {
        List<Order> orders = new List<Order>
        {
            new Order
            {
                OrderID = "orderID1",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU1",
                        Quantity = 1
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU2",
                        Quantity = 2
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU3",
                        Quantity = 3
                    }
                }
            },
            new Order
            {
                OrderID = "orderID2",
                OrderLines = new List<OrderLine>
                {
                    new OrderLine
                    {
                        ProductSKU = "SKU4",
                        Quantity = 4
                    },
                    new OrderLine
                    {
                        ProductSKU = "SKU5",
                        Quantity = 5
                    }
                }
            }
        };

        //required result is the list of all SKUs in orders
        List<string> allSKUs = new List<string>();

        //With Select case 2 foreach loops are required
        var flattenedOrdersLinesSelectCase = orders.Select(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectCase)
        {
            foreach (OrderLine orderLine in flattenedOrderLine)
            {
                allSKUs.Add(orderLine.ProductSKU);
            }
        }

        //With SelectMany case only one foreach loop is required
        allSKUs = new List<string>();
        var flattenedOrdersLinesSelectManyCase = orders.SelectMany(o => o.OrderLines);
        foreach (var flattenedOrderLine in flattenedOrdersLinesSelectManyCase)
        {
            allSKUs.Add(flattenedOrderLine.ProductSKU);
        }

       //If the required result is flattened list which has OrderID, ProductSKU and Quantity,
       //SelectMany with selector is very helpful to get the required result
       //and allows avoiding own For loops what according to my experience do code faster when
       // hundreds of thousands of data rows must be operated
        List<OrderLineForReport> ordersLinesForReport = (List<OrderLineForReport>)orders.SelectMany(o => o.OrderLines,
            (o, ol) => new OrderLineForReport
            {
                OrderID = o.OrderID,
                ProductSKU = ol.ProductSKU,
                Quantity = ol.Quantity
            }).ToList();
    }
}
class Order
{
    public string OrderID { get; set; }
    public List<OrderLine> OrderLines { get; set; }
}
class OrderLine
{
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}
class OrderLineForReport
{
    public string OrderID { get; set; }
    public string ProductSKU { get; set; }
    public int Quantity { get; set; }
}

-2

このSelectManyメソッドIEnumerable<IEnumerable<T>>IEnumerable<T>、共産主義のようににノックダウンします。すべての要素は同じように動作します(愚かな人は天才的な人と同じ権利を持っています)。

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }

-5

それは私が理解する最良の方法です。

            var query =
            Enumerable
                .Range(1, 10)
                .SelectMany(ints => Enumerable.Range(1, 10), (a, b) => $"{a} * {b} = {a * b}")
                .ToArray();

        Console.WriteLine(string.Join(Environment.NewLine, query));

        Console.Read();

乗算表の例。


4
「最高」の意味が劇的に変わった場合のみ。
Vahid Amiri、2016年

2
これはあなたが思う最高の方法ですか?では難しい考え方は何ですか?
Syed Ali
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.