tags:

views:

69

answers:

4

What is the C# equivalent of this pseudo-code?

var pattern = ...;
var lookup = new Dictionary<string, string>();

foreach (var group in new Regex(pattern).Matches())
{
    lookup[group.Name] = group.Value;
}

I don't see any System.Text.RegularExpressions group-related object that exposes the group name.

What am I missing?

What I'm actually trying to do is convert a file with lines in this format:

eventName|message|date

To an IEnumerable<EventLogLine>, with EventLogLine being:

public struct EventLogLine
{
    public string EventName { get; set; }
    public string Message { get; set; }
    public DateTime Date { get; set; }
}

And put those lines into a IDictionary<string /*EventName*/, IEnumerable<EventLogLine>>.

A: 

I just knocked this up in using LINQ. It relies on the List<string> to be filled with the lines in the file.

        var lines = new List<string>();
        var dict = lines.Select(l =>
        {
            var sp = l.Split('|');
            return new EventLogLine { EventName = sp[0], Message = sp[1], Date = DateTime.Parse(sp[2]) };
        })
        .GroupBy(e => e.EventName)
        .ToDictionary(grp => grp.Key, grp => grp.AsEnumerable());

Basically you convert each line to an EventLogLine, using the Select(), then use the GroupBy() to create your grouping based on EventName, then using the ToDictionary() to run the query and create your dictionary in the format required!

Alastair Pitts
A: 

See the example in the Match.Groups MSDN article. I think you should look at Alastair's answer though, seeing as your input is so simple it would probably be easier to read the code later if you just use ReadLine and Split.

Jake T.
A: 

Consider using ToLookup rather than ToDictionary. Lookups work naturally with linq and generic code in general by being immutable and by exposing a a very simple API. Also, I would encapsulate the parsing into the EventLogLine struct.

As a result, the code would look like this:

IEnumerable<string> lines;

ILookup<string, EventLogLine> lookup = 
    lines.Select(EventLogLine.Parse).ToLookup(evtLine => evtLine.EventName);

An example consumer:

if(lookup["HorribleEvent"].Any())
    Console.WriteLine("OMG, Horrible!");

foreach(var evt in lookup["FixableEvent"])
    FixIt(evt);

var q = from evtName in relevantEventNames
        from evt in lookup[evtName]
        select MyProjection(evt);

Note that you do not need to check for key-existance, unlike for a Dictionary:

if(dictionary.ContainsKey("HorribleEvent")) //&& dictionary["HorribleEvent"].Any() sometimes needed
    Console.WriteLine("OMG, Horrible!");

if(dictionary.ContainsKey("FixableEvent"))
    foreach(var evt in lookup["FixableEvent"])
        FixIt(evt);

var q = from evtName in relevantEventNames.Where(dictionary.ContainsKey)
        from evt in dictionary[evtName]
        select MyProjection(evt);

As you may notice, working with a dictionary containing IEnumerable values introduces subtle friction - ILookup is what you want!

Finally, the modified EventLogLine:

public struct EventLogLine {
    public string EventName { get; private set; }
    public string Message { get; private set; }
    public DateTime Date { get; private set; }

    public static EventLogLine Parse(string line) {
        var splitline = line.Split('|');
        if(splitline.Length != 3) throw new ArgumentException("Invalid event log line");
        return new EventLogLine { 
            EventName = splitline[0],
            Message = splitline[1],
            Date = DateTime.Parse(splitline[2]),
        };
    }
}
Eamon Nerbonne
A: 

To answer this part of your question:

I don't see any System.Text.RegularExpressions group-related object that exposes the group name. What am I missing?

I have adapted Eamon Nerbonne's struct to use regular expressions:

public struct EventLogLine
{
    public string EventName { get; private set; }
    public string Message { get; private set; }
    public DateTime Date { get; private set; }

    private static Regex expectedLineFormat = new Regex(
            @"^(?<eventName>[^|]*)\|(?<message>[^|]*)\|(?<date>[^|]*)$",
            RegexOptions.Singleline | RegexOptions.Compiled
    );

    public static EventLogLine Parse(string line) {

        Match match = expectedLineFormat.Match(line);

        if (match.Success) {
            return new EventLogLine {
                EventName = match.Groups["eventName"].ToString(),
                Message = match.Groups["message"].ToString(),
                Date = DateTime.Parse(match.Groups["date"].ToString()
            };
        }
        else {
            throw new ArgumentException("Invalid event log line");
        }
    }
}
Buh Buh