Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split a string into a tuple using LINQ?

Tags:

c#

linq

Given a string like "name:Dave" that I'd like to convert to a Tuple<string,string> is there a neat way to do this inline using LINQ, rather than first getting the array then parsing it?

I am parsing some config data from my app.config file like this:

<Data>
  <add key="John" value="instrument:guitar"/>
  <add key="Paul" value="instrument:bass"/>
  <add key="George" value="instrument:guitar"/>
  <add key="Ringo" value="instrument:drums"/>
</Data>

Which I want to convert to a Dictionary<string,tuple<string,string>> (I know this is an odd data structure!)

Starting with some code:

            var x = ((ConfigurationManager.GetSection("Data") as Hashtable))
                .Cast<System.Collections.DictionaryEntry>()
                .ToDictionary(n => n.Key.ToString(), n => something(n.Value.ToString().Split(':')));

I wondered if there's a neat LINQ way to do this in-line rather than write a utility function to get from String.Split's array into a single Tuple?

like image 565
Mr. Boy Avatar asked Feb 09 '17 13:02

Mr. Boy


2 Answers

var source = new Dictionary<string, string>(){ { "John", "instrument:guitar" } };
var tuple = source.Select(x => new { key = x.Key, values = x.Value.Split(':') }).Select(y => new Tuple<string, string>(y.key, y.values[1])).ToList();
like image 97
Ben Avatar answered Sep 28 '22 01:09

Ben


Your string is basically an XML so I would just parse this using XmlDocument from System.Xml namespace. Then you can just use GetElementsByTagName because all values you want to retrieve has the same tag ( <add /> ) and then just use it to create a dictionary like such :

string @xml = @"<Data>
<add key=""John"" value=""instrument:guitar""/>
<add key=""Paul"" value=""instrument:bass""/>
<add key=""George"" value=""instrument:guitar""/>
<add key=""Ringo"" value=""instrument:drums""/>
</Data>";

XmlDocument doc = new XmlDocument();
doc.LoadXml(@xml);
var dict = new Dictionary<string, Tuple<string, string>>();
doc.GetElementsByTagName("add")
    .OfType<XmlElement>()
    .Select(element =>
    {
        string key = element.Attributes["key"].Value;
        string[] value = element.Attributes["value"].Value.Split(':');
        dict.Add(key, new Tuple<string, string>(value[0], value[1]));
        return dict.Last();
    });
return dict;

Which can be shortened into something like this :

XmlDocument doc = new XmlDocument();
doc.LoadXml(@xml);
Dictionary<string, Tuple<string, string>> dict = doc.GetElementsByTagName("add")
    .OfType<XmlElement>()
    .Select(element => new Tuple<string, string[]>(element.Attributes["key"].Value, element.Attributes["value"].Value.Split(':')))
    .ToDictionary(element => element.Item1, element => new Tuple<string, string>(element.Item2[0], element.Item2[1]));

return dict;

Or even shorter :

XmlDocument doc = new XmlDocument();
doc.LoadXml(@xml);
Dictionary<string, Tuple<string, string>> dict = doc.GetElementsByTagName("add")
    .OfType<XmlElement>()
    .ToDictionary(element => element.Attributes["key"].Value, element => new Tuple<string, string>(element.Attributes["value"].Value.Split(':')[0], element.Attributes["value"].Value.Split(':')[1]));
return dict;

EDIT : ( After question edit )

You can use System.Configuration library which contains ConfigurationManager object and do it like such :

ConfigurationManager.AppSettings.AllKeys.ToDictionary(
    key =>
        key,
    key =>
        new Tuple<string, string>(
            ConfigurationManager.AppSettings[key].Split(':')[0],
            ConfigurationManager.AppSettings[key].Split(':')[1]
        )
);

But you have to make sure that app.config contains only the keys you want to process using this method. If you try to process line which looks like <add key="Willy" value="noinstrument" /> that method will break.

like image 40
Mateusz Avatar answered Sep 27 '22 23:09

Mateusz