3

Given

string[] array = new string[]
{
   "Sample1:foo",
   "Sample2:bar",
   "Sample1:foo1"
}

I know I can convert it to a dictionary this way:

Dictionary<string, string> whatever = new Dictionary<string, string>
foreach (string s in array) do...
  string sampleNumber = s.Substring(0, indexOfColon);
  string fooOrBar= s.Substring(indexOfColon + 1);
  whatever[sampleNumber] = fooOrBar;

And this will prevent an aggregate exception being thrown when a duplicate key is added (although overriding the key, which is fine in this case). Can I do this with LINQ? I am trying something along the lines of:

Dictionary<string, string> whatever = array.ToDictionary(
 key => key.Split(':')[0], value => value.Split(':')[1]);

Is there a way to do this without creating a lookup beforehand?

1
  • In the case you want to keep the duplicates, you can use List<Tuple<string, string>> with a one-liner List<Tuple<string, string>> whatever = array.Select(key => new Tuple<string, string>(key.Split(':')[0], key.Split(':')[1])).ToList(); Commented Sep 19, 2017 at 3:00

4 Answers 4

4

Try this:

Dictionary<string, string> whatever =
    array
        .Reverse()
        .GroupBy(key => key.Split(':')[0])
        .SelectMany(x => x.Take(1))
        .ToDictionary(key => key.Split(':')[0], value => value.Split(':')[1]);

It gives:

whatever

Or you could do this:

Dictionary<string, string> whatever =
    array
        .Aggregate(
            new Dictionary<string, string>(),
            (d, v) => { d[v.Split(':')[0]] = v.Split(':')[1]; return d; });

Same result.

Sign up to request clarification or add additional context in comments.

3 Comments

Worked perfect.
Great idea! An alternative, possibly shorter method than using reverse and select many, is to just use Last(), not sure about the time difference, but var whatever = array.GroupBy(p => p.Split(':')[0]).ToDictionary(key => key.Key, value => value.Last().Split(':')[1]);.
@KeyurPATEL - I like your approach, but I do try to choose options that avoid .Last() or .First(). I know it doesn't matter with a .GroupBy(...) but I like keeping a good habit.
2

What you want is definitely not a Dictionary<string, string> because keys are duplicated. It can be a Dictionary<string, List<string>>, but IMO it's much more like a a ILookup<string, string>:

var result = array
            .Select(a =>
            {
                var parts = a.Split(':');
                return new
                {
                    Key = parts[0],
                    Value = parts[1]   //maybe you need to check something here
                };
            })
            .ToLookup(o => o.Key, o => o.Value);

Comments

1

I am not sure how you deal with the value , maybe you can try this:

var list = array.Select(m => new { Key = m.Split(':')[0], Value = m.Split(':')[1] })
            .GroupBy(m => m.Key)
            .ToDictionary(m => m.Key, m => string.Join(",", m.Select(p => p.Value)));

Comments

1

I am sad how nobody cares about performace. Wasteful allocations, wasteful calculations. I tested accepted answer against this code on strings with 50% key collisions:

char[] separatorArray = { ':' }; // do not create new array on every single item of the array
HashSet<string> dedupl = new HashSet<string>();

var res = array
    .Select(s => s.Split(separatorArray))
    .Where(x => dedupl.Add(x[0]))
    .ToDictionary(x => x[0], x => x[1]);

it is roughly 2,5x faster and uses half of the memory (I measured top memory usage).

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.