Bryan B
Bryan B

Reputation: 4535

LINQ aggregating multiple IEnumerables into one?

I have a Dictionary defined as <int, IEnumerable<char>>. Lets say the data is populated like:

1, a b c d e
2, f g h i j
3, k l m n o

If I have an IEnumerable consisting of 1 & 3, what would the LINQ look like to return 'a b c d e k l m n o' (assuming space represents an iteration).

Upvotes: 2

Views: 547

Answers (6)

James Manning
James Manning

Reputation: 13579

SelectMany is indeed what you want, but IMHO it's more readable to use the comprehension syntax and let it do the mapping for you, so something like:

var dict = ... // dictionary
var keys = ... // enumerable with 1 and 3 in it
var result = from key in keys
             from val in dict[key]
             select val;

It's easier (again, IMHO) to toss an 'orderby' in there as well if/as needed.

Of course, if you find the extension method version simpler to read/parse, then by all means, use that instead. :)

Upvotes: 5

JaredPar
JaredPar

Reputation: 754545

It's unclear if you want only the 1 & 3 lines or all of them (1, 2 and 3). Here's the solution for both

Dictionary<int, string> map = ...;
// 1 and 3 only
IEnumerable<char> result1 = map
  .Where(x => x.Key == 1 || x.Key == 3)
  .OrderyBy(x => x.Key)
  .SelectMany(x => x.Value)

// All
IEnumerable<char> result2 = map
  .OrderyBy(x => x.Key)
  .SelectMany(x => x.Value)

Upvotes: 0

Mark Byers
Mark Byers

Reputation: 837936

If you want a KeyNotFoundException if a key is not found:

IEnumerable<char> result = keys.SelectMany(key => d[key]);

If you want to silently ignore keys that are not found:

IEnumerable<char> result = keys.Where(key => d.ContainsKey(key))
                               .SelectMany(key => d[key]);

Upvotes: 3

Adam Maras
Adam Maras

Reputation: 26843

You need a Where clause to filter the keys contained in the dictionary, and a SelectMany clause to get a single enumerable list from each list contained in the dictionary.

Dictionary<int, IEnumerable<char>> dict; // contains all of your key-value pairs
IEnumerable<int> keys; // contains the keys you want to filter by

IEnumerable<char> data = dict.Where(kvp => keys.Contains(kvp.Key))
                             .SelectMany(kvp => kvp.Value);

// or, alternatively:

IEnumerable<char> data = keys.SelectMany(i => dict[i]);

Note that the second query will throw an exception if you have a key in the keys enumerable that doesn't exist in your dictionary.

Upvotes: 0

user153498
user153498

Reputation:

As you mentioned in your comment, you can use a SelectMany to aggregate an IEnumerable of IEnumerables into a single IEnumerable. However, you can also use Concat to combine two separate collections into a single selection (or you can chain it to combine as many as you'd like).

Upvotes: 0

Chris Shain
Chris Shain

Reputation: 51319

Assuming that you have

var someEnumerables = new Dictionary<int, IEnumerable<char>>();

then

// Flattened will be an enumeration of the values ordered first by the key
var flattened = someEnumerables.OrderBy(kvp => kvp.Key).SelectMany(kvp => kvp.Value)

Upvotes: 0

Related Questions