e0x3
e0x3

Reputation: 131

filling List<Tuple<string,string>> with linq

There is a list of string like this

{'123',
 '234',
 '345',
 '123c',
 '456',
 '234c'}

and the task is to create a list<Tuple<string><string>> and fill every tuple so that the first value some 'x' and the second value 'xc' for example Tuple<123,123c> , if there is not a pair ending with 'c' char, just put into the second value null, like in a tuple Tuple<345,null>. Is there a way to do that with linq?.

Upvotes: 0

Views: 1708

Answers (4)

Matthew Watson
Matthew Watson

Reputation: 109577

You can group all the strings by their values with trailing 'c' removed, and then output the tuples based on whether the count is greater than 1.

This is a C# 7.x/.Net 4.7 solution that uses the new Tuple syntax:

var result = strings
    .GroupBy(x => x.TrimEnd('c'))
    .Select((g, v) => (g.Key, g.Count() > 1 ? g.Key+'c': null));

Here's the same approach using older versions of C# and .Net:

var result = strings
    .GroupBy(x => x.TrimEnd('c'))
    .Select((g, v) => Tuple.Create(g.Key, g.Count() > 1 ? g.Key+'c': null));

Note that using g.Count() > 1 to determine if a sequence contains more than one element is not normally as efficient as using g.Skip(1).Any() (unless the underlying sequence is a List or an array), but for this specific application it will be as efficient since there are never more than 2 items in the collection.

Upvotes: 1

Fruchtzwerg
Fruchtzwerg

Reputation: 11389

Here is a very simple solution generating the tuples after splitted into two groups:

var firsts = strings.Where(s => !s.EndsWith("c"));
var seconds = strings.Where(s => s.EndsWith("c"));

var result = firsts.Select(f =>
    new Tuple<string, string>(f, seconds.FirstOrDefault(s => s.Replace("c", "") == f)));

Dividing the code into these steps makes it easy to read and understand.

Upvotes: 1

Tez LaCoyle
Tez LaCoyle

Reputation: 224

If all your input strings are a string of digits with an optional c at the end, you could try this:

var result = list.GroupBy(x => x.TrimEnd('c'))
                 .Select(g => Tuple.Create(g.Key, g.FirstOrDefault(v => v.EndsWith("c"))))

This will group them by the string of digits, having excluded any c at the end, and then create a tuple whose first item is that string of digits, and whose second item is that string with c appended if it was in the list or null if not.

Upvotes: 0

Christos
Christos

Reputation: 53958

You could try something like this:

var result = list.GroupBy(word => !word.EndsWith('c'))
                 .Select(gr => Tuple.Create(gr.Key, gr.FirstOrDefault(i=>i.Contains(gr.Key))));

Essentially, you group by the list elements based on if they word ends with the caracter 'c'. So you would have a sequence of keys, which would be words that doesn't end with the character 'c' and an associated sequence for each of this keys with the words that end with 'c'. Then you project the result in sequence of Tuples with the first element to be the key of the gropu and the second element to be the corresponding value that ends with 'c' if that is exists at all or null if it doesn't exists.

Upvotes: 1

Related Questions