KBD

Keith Devens .com

Thursday, January 8, 2009 Flag waving
Communicating badly and then acting smug when misunderstood is not cleverness. – Randall Munroe (xkcd)

Tag: LINQ

Daily link icon Wednesday, August 20, 2008

Bad C# design

I should be able to do something like:

var table = new HtmlTable {
  Rows = { // <-- error is here
    from XmlNode p in node.SelectNodes("./property")
    select new HtmlTableRow{
      Cells = {
        new HtmlTableCell{ InnerText = Server.HtmlEncode(p.Attributes["description"].Value) },
        new HtmlTableCell{ InnerText = Server.HtmlEncode(p.InnerText) }
      }
    }
  }
};

But I can't, because the LINQ code isn't "expanded" so the compiler complains that while it expects an HtmlTableRow element between the braces assigned to Rows, you're giving it a collection. But, you can't assign the collection to Rows directly because it's a read-only property.

Bad class design (Rows being read-only... same as Cells fwiw) or bad language design in that there's no way to "expand" the results of the LINQ query to do what seems natural in that spot?

Instead, I have to do:

var rows = 
  from XmlNode p in node.SelectNodes("./property")
  select new HtmlTableRow{
    Cells = {
      new HtmlTableCell{ InnerText = Server.HtmlEncode(p.Attributes["description"].Value) },
      new HtmlTableCell{ InnerText = Server.HtmlEncode(p.InnerText) }
    }
  };

var table = new HtmlTable();
foreach (var r in rows)
  table.Rows.Add(r);

which is redundant.

Daily link icon Thursday, July 24, 2008

  1. LINQ to Objects - 5 Minute Overview - Hooked on LINQ. Decent tutorial. Has examples of grouping and joins.

       (0) Tags: [C#, LINQ]
  2. Download LINQPad (via).

       (0) Tags: [LINQ, Software, SQL]
  3. C# 3.0: The Evolution Of LINQ And Its Impact On The Design Of C# (via). Very interesting explanation of how LINQ came about.

       (0) Tags: [C#, LINQ]
  4. New "Orcas" Language Feature: Extension Methods - ScottGu's Blog. Very informative post.

    Edit: He also covers C# 3's query syntax (i.e. LINQ).

       (0) Tags: [C#, LINQ]

Joining together the results of a LINQ query into a string

Say you want to search a string with a regular expression and return all the matches concatenated together, using LINQ:

var str = "some string";
var matches = Regex.Matches(str, @"REGEX");

Three ways to concatenate:

Using String.Join (simplest):

var foo = String.Join("", (from Match match in result select match.Value).ToArray());

Using an accumulator:

var foo = (from Match match in matches select match.Value)
    .Aggregate(new StringBuilder(), (sb, s) => sb.Append(s)).ToString();

Using a loop:

var sb = new StringBuilder();
foreach (var s in (from Match match in result select match.Value)){
    sb.Append(s);
}
var foo = sb.ToString();

First seems most concise, and it's easier to specify a join character with. Apparently there's no way to massage an IEnumerable(T) into a String.Join, so unfortunately you need to pass it an array, which makes the IEnumerable fill out all its values into an array (so it can't be lazy), and then String.Join makes another pass over that array and copies the values into a string. So it uses double the space and double the time.

The second is more obscure, but only makes one pass over the result, though it's harder to specify a join character if desired.

The third is presumably most efficient, but it's more verbose.

In conclusion, there should be a String.Join(IEnumerable<T>).

Edit: Though it's impossible to define a "static" extension method (like, another variation of String.Join), you can define a Join method on IEnumerable like so:

static class Extentions{
    public static string Join(this IEnumerable source, string separator){
        var sb = new StringBuilder();
        bool first = true;
        foreach(var foo in source){
            if(!first)
              sb.Append(separator);

            sb.Append(foo.ToString());
            first = false;
        }
        return sb.ToString();
    }
}

The first example above now becomes:

var foo = (from Match match in result select match.Value).Join("");

or even more concisely:

var foo = Regex.Matches(str, @"REGEX").Join("");
// (apparently the ToString on a Match object returns its Value)

Very cool.

Note: Works with custom ToString()s as expected:

class Custom{
    public string Value { get; set; }
    public override string ToString(){
        return "VALUE: "+Value;
    }
}
var list = new List<Custom> {
    new Custom { Value = "one" },
    new Custom { Value = "two" }
};

Console.WriteLine(list.Join(", "));

prints "VALUE: one, VALUE: two" as expected.

Final note: I would have used FirstOrDefault in my Join extension method instead of a first boolean, but the example of the regular expression object was chosen because it's not a generic, so I can't use IEnumerable<T>, only IEnumerable.

January 2009
SunMonTueWedThuFriSat
 123
45678910
11121314151617
18192021222324
25262728293031



RSS feed RSS feed for Keith's Weblog
Atom feed Atom feed for Keith's Weblog
Weblog archive
Recent comments
  on 4 posts

Recent comments XML

new⇒The Elegant Universe

Well I have finally found the crazy​guy that preaches useless nonsence​in A...

Joseph Baxter: Jan 7, 11:07pm

I hate Norton Antivirus


SYMANTEC is very​cunning..
Symantec now have a​redeemable cash back offe...

CAN: Jan 4, 6:25pm

Spider solitaire

Hi everyone!

Glad to have found​this site.  I have enjoyed reading​the c...

flwrchld53: Jan 4, 5:30pm

The Escaped Prisoner: When God Is a Monster

if islam is afraid of one woman, it​is sad. it is sad that a lot of​muslim ...

alex: Jan 2, 1:56pm

Generated in about 0.162s.

(Used 10 db queries)

mobile phone