2
\$\begingroup\$

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

\$\endgroup\$
4
  • 2
    \$\begingroup\$ I don't see a hierarchical list here — I see a bunch of empty keys (and your code depends on the ordering of the array). Do you have any control over the creation of the array, and can you show the code for that? Or is this really how the array is filled? \$\endgroup\$
    – Adam
    Commented Aug 14, 2012 at 12:03
  • \$\begingroup\$ Yup, it is deterministically ordered. It is directly transformed from an excel sheet. The excel list is grouped and I need to "pad" the empty entries with the last value on that column. Granted that the list type is not hierarchical, but the data is grouped. \$\endgroup\$
    – Tormod
    Commented Aug 14, 2012 at 12:07
  • \$\begingroup\$ Do you need that Values there? Can't you compute them? They seem to be 1,2,3,…,10. \$\endgroup\$
    – svick
    Commented Aug 14, 2012 at 12:29
  • \$\begingroup\$ The live data are not like that. I just created test data in sequence so that it would be easier to see errors in the algorithm. Otherwise, sure, I would use the overload of Select() that provides a running counter. \$\endgroup\$
    – Tormod
    Commented Aug 14, 2012 at 12:33

2 Answers 2

3
\$\begingroup\$

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?

\$\endgroup\$
1
\$\begingroup\$

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;
    }
);
\$\endgroup\$
1
  • \$\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
    Commented Sep 3, 2012 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.