tags:

views:

134

answers:

6

I want to rewrite this, maybe using predicates, or Lambda, how can I do this?

        serviceResponse = client.Authorize(....);


        string authorizeResponse = string.Empty;

        foreach (CdcEntry element in serviceResponse.Cdc)
        {
            authorizeResponse += element.Name + Environment.NewLine;
            foreach (CdcEntryItem item in element.Items)
            {
                authorizeResponse += " (" + item.Key + ", " + item.Value + ") " 
                + Environment.NewLine;
            }
        }

Thanks

A: 

You certainly could but all you would gain would be a couple of ForEach method calls and multi-statement lambdas inside which (in my opinion) would be significantly less readable.

Without knowing the datatypes of you identifiers (specifically serviceResponse.Cdc and element.Items) you may even have to roll your own ForEach extension method as well to make this work.

I think what you have is better than a LINQ equivalent but if you really want to LINQify it, please post some more info about the code you have posted and we may be able to help more.

Andrew Hare
+5  A: 

I don't think there's any real point to use LINQ here, since you're trying to build a response string, and are thus performing a task most suited to imperative rather than functional programming. (You could of course use the Aggregate method on a string, but that's not really any help.)

In addition, the StringBuilder type is more suited for this sort of job. (It's much more efficient, and also cleaner in my opinion. Of course, the former may not matter so much depending on the context.)

Here is the optimal way to do this, in my view:

serviceResponse = client.Authorize(....);

var sb = new StringBuilder();

foreach (CdcEntry element in serviceResponse.Cdc)
{
    sb.AppendLine(element.Name);
    foreach (CdcEntryItem item in element.Items)
    {
        sb.AppendFormat(" ({0}, {1}) ", item.Key, item.Value);
        sb.AppendLine();
    }
}

var authorizeResponse = sb.ToString();
Noldorin
I would say that StringBuilder() is a lot of overkill unless he has more than a few dozen of these elements and items. (Of course, maybe he does.)
mquander
@mquander: It's not overkill, because it doesn't add any more code! In fact, it even reduce the length of code slightly, and moreover, it's significantly more readable in my opinion.
Noldorin
Different strokes for different folks, I guess.
mquander
A: 

This should roughly do it:

x = x.Aggregate("",
    (cdc, s) => s + string.Format("{0}{1}",
                    cdc.Name, Environment.NewLine) +

        cdc.Aggregate("",
           (item, s) => s + string.Format(" ({0}, {1}) {2}",
                            item.Key, item.Value, Environment.NewLine)));

Frankly, I am a big functional guy, but multiple nested folds makes me think too hard for a simple problem like this, especially with C#'s verbose syntax. I would not even use this. I would use your foreach version.

mquander
This could be problematic with string concatenation. But you can use Aggregate and pass a StringBuilder instead... But you're right, tyhis is not a use case where LINQ shines.
Yann Schwartz
Yeah, I initially considered almost exactly the same code, but also agree with your statement that this isn't so readable.
Noldorin
I also then considered using a StringBuilder in aggregate, but that's just plain horrible - it means your lambda expressions then become impure functions, and the code looks messy.
Noldorin
Yep. It defeats the purpose. If you want to have side effects, we might as well use a ForEach extension method or, well, using the foreach construct. Oh, wait...
Yann Schwartz
This is what I meant when I said "significantly less readable":)
Andrew Hare
A: 

You could gain a lot by using string builder instead of string concatenation. Dont see any performance benefit in LINQification.

 StringBuilder sb = new StringBuilder()

 foreach (CdcEntry element in serviceResponse.Cdc)
 {
     sb.AppendLine(element.Name);
     foreach (CdcEntryItem item in element.Items)
     {
        sb.AppendLine(" ({0},{1}) ", item.Key, item.Value);
      }
 }
Shafqat Ahmed
AppendLine doesn't have an overload that accepts a param array. Otherwise, identical to my answer.
Noldorin
Sorry, should be append format..sb.AppendFormat(" ({0},{1}) \n", item.Key, item.Value);
Shafqat Ahmed
A: 

You can certainly use LINQ to fold the list. But you should keep in mind that language features like LINQ are there to make things simpler and clearer. I'm not sure the logic below is either:

StringBuilder sb = new StringBuilder();
string authorizeResponse = serviceResponse.Aggregate( sb, 
                              (q,x) => q.Append( string.Format( "{0}{1}{2}", x.Name, Environment.NewLine,
                              x.Aggregate( q, (y,z) => y.Append( string.Format("({0}){1}", z.Key, Environment.NewLine) ) ) );

string authorizeResponse = sb.ToString();
LBushkin
A: 
        var result =
                elements.Select(
                        element =>
                        new[] {element.Name}.Concat(
                                element.Items.Select(item => "({0},{1})".With(item.Key, item.Value)))).SelectMany(
                        item => item).Join(Environment.NewLine);