The following code is pretty ugly. I suspect that there is a more functional and elegant way of achieving this result.
var lines =
new[]{
new{Head="A",Value="1"},
new{Head="",Value="2"},
new{Head="",Value="3"},
new{Head="",Value="4"},
new{Head="B",Value="5"},
new{Head="B",Value="6"},
new{Head="C",Value="7"},
new{Head="",Value="8"},
new{Head="D",Value="9"},
new{Head="",Value="10"}};
string currentHead="";
string currentValue="";
lines.Select(line=>{
string newHead;
string newValue;
if (String.IsNullOrEmpty(line.Head))
{
newHead = currentHead;
} else
{
newHead = line.Head;
currentHead = line.Head;
}
if (String.IsNullOrEmpty(line.Value))
{
newValue = currentValue;
} else
{
newValue = line.Value;
currentValue = line.Value;
}
return new{Head=newHead,Value=newValue};}).ToList();
The desired output is to produce
A,1
A,2
A,3
A,4
B,5
B,6
C,7
C,8
D,9
D,10
2 Answers 2
You need to look through your code with an analytical eye and discover the pattern that you are implementing. I call it default if (null or) empty.
So I created an extension method to implement that pattern.
public static string DefaultIfEmpty(this string source, string defaultValue)
{
return string.IsNullOrEmpty(source) ? defaultValue : source;
}
(Note of caution: you may want to choose a different name for it since this method already exists in LINQ; technically, this is an overload, which could create problems if a future version of C# adds this signature.)
This makes it possible to adapt the LINQ expression:
var currentHead = string.Empty;
var currentValue = string.Empty;
var result = lines.Select(
line =>
{
currentHead = line.Head.DefaultIfEmpty(currentHead);
currentValue = line.Value.DefaultIfEmpty(currentValue);
return new { Head = currentHead, Value = currentValue };
}).ToList();
Sweet, isn't it?
Another options would be to use Aggregate():
var result = lines.Aggregate(
seed: lines.Take(0).ToList(), // get an empty list of the anonymous type
func: (list, line) => {
list.Add(new {
// you can implement a fast Last() method for lists or use list[list.Count - 1] if list is long/you're worried about efficiency
Head = string.IsNullOrEmpty(line.Head) ? list.Last().Head : line.Head,
Value = string.IsNullOrEmpty(line.Value) ? list.Last().Value : line.Value
});
return list;
}
);
-
\$\begingroup\$ Very nice trick in generating an empty list of an anonymous type (the seed). More efficient with captured variable in the object initializer: "Head = (String.IsNullOrEmpty(line.Head))?currentHead : (currentHead=line.Head)," \$\endgroup\$Tormod– Tormod2012年09月03日 12:51:03 +00:00Commented Sep 3, 2012 at 12:51
Value
s there? Can't you compute them? They seem to be 1,2,3,...,10. \$\endgroup\$