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:
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:
Some usage example:
Now a more nicer projection:
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.
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
Post a Comment