SelectMany - projecting the index of the result

There are 4 overloads of the SelectMany method. Two of them project the index of each source element, for example:

string[] sentenceSequences = new string[] {"The quick brown", "fox jumped over","the lazy dog."};
sentenceSequences.SelectMany(
// index - the position of the sequence in the sentenceSequences array
(sequence, index) =>
// check if index is on odd position and if so call ToUpper()
// ... put the sequence in other array, as the result
index % 2 == 0 ? new [] {sequence.ToUpper()} : new [] { sequence }
)
.Dump();


But there is no overload for projecting the index of the result also (the index of the element in the result sequence). So here an implementation of it:


public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, int, TResult> resultSelector)
{
int collectionSelectorIndex = -1;
foreach (TSource sourceCurent in source)
{
collectionSelectorIndex++;
int resultSectorIndex = -1;
foreach (TCollection resultCurrent in collectionSelector(sourceCurent, collectionSelectorIndex))
{
resultSectorIndex++;
yield return resultSelector(sourceCurent, resultCurrent, resultSectorIndex);
}
}
yield break;
}

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, int, TResult> resultSelector)
{
foreach (TSource sourceCurent in source)
{
int resultSectorIndex = -1;
foreach (TCollection resultCurrent in collectionSelector(sourceCurent))
{
resultSectorIndex++;
yield return resultSelector(sourceCurent, resultCurrent, resultSectorIndex);
}
}
yield break;
}


Some usage example:


sentenceSequences.SelectMany(
sequence => sequence.Split(' '),
(sequence, word, index) => new {sequence, word, index}
).SelectMany(
word => word.word.ToCharArray(),
(word, chr, index) => new {word.sequence, word.word, wordindex = word.index, @char = chr, charindex = index}
)
.Dump();

sentence word wordindex char charindex
The quick brown The 0 T 0
The quick brown The 0 h 1
The quick brown The 0 e 2
The quick brown quick 1 q 0
The quick brown quick 1 u 1
The quick brown quick 1 i 2
The quick brown quick 1 c 3
The quick brown quick 1 k 4
The quick brown brown 2 b 0
The quick brown brown 2 r 1
The quick brown brown 2 o 2
The quick brown brown 2 w 3
The quick brown brown 2 n 4
fox jumped over fox 0 f 0
fox jumped over fox 0 o 1
fox jumped over fox 0 x 2
fox jumped over jumped 1 j 0
fox jumped over jumped 1 u 1
fox jumped over jumped 1 m 2
fox jumped over jumped 1 p 3
fox jumped over jumped 1 e 4
fox jumped over jumped 1 d 5
fox jumped over over 2 o 0
fox jumped over over 2 v 1
fox jumped over over 2 e 2
fox jumped over over 2 r 3
the lazy dog. the 0 t 0
the lazy dog. the 0 h 1
the lazy dog. the 0 e 2
the lazy dog. lazy 1 l 0
the lazy dog. lazy 1 a 1
the lazy dog. lazy 1 z 2
the lazy dog. lazy 1 y 3
the lazy dog. dog. 2 d 0
the lazy dog. dog. 2 o 1
the lazy dog. dog. 2 g 2
the lazy dog. dog. 2 . 3


Now a more nicer projection:


sentenceSequences.SelectMany(
sequence => sequence.Split(' '),
(sequence, word, index) => new {sequence, word, index}
).SelectMany(
word => word.word.ToCharArray(),
(word, chr, index) => new {
// show the sequence only when the word and the char are the first line in their sequences
sequence = word.index == 0 && index == 0 ? word.sequence : null,
// show the word only when the projected char is the first one in its sequence
word = index == 0 ? word.word : null,
@char = chr,
}
).Dump()



sequence word char
The quick brown The T
h
e
quick q
u
i
c
k
brown b
r
o
w
n
fox jumped over fox f
o
x
jumped j
u
m
p
e
d
over o
v
e
r
the lazy dog. the t
h
e
lazy l
a
z
y
dog. d
o
g
.


This might be a good solution when I need to flatten a list of nested objects with some kind large depth (Customers -> Orders -> OrderItems -> OrderItemsHistory -> Updaters ). Flattening this structure means most of the properties get repeating themselves and I end up to use either the objects themselves to create a report with a tree of repeaters or to manipulate a single repeater and decide when to show a property or not.


The last example show how easy is to "hide" the flattened properties when the results selector of the SelectMany projects the index of the result item also.

Comments

Popular posts from this blog

IIS 7.5, HTTPS Bindings and ERR_CONNECTION_RESET

Verify ILogger calls with Moq.ILogger

Table Per Hierarchy Inheritance with Column Discriminator and Associations used in Derived Entity Types