Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ - Query to combine 3 datasets - improvements

Tags:

c#

.net

linq

The code below will combine 3 separate tables into one based on the order of importance. I am looking to improve this - perhaps by using query syntax to avoid the intermediate stage. Are there different (better?) ways of achieving the same result?

var upsert = new List<KeyValuePair<string, string>>() {
    new KeyValuePair<string, string>("f1","f1-upsert"),
    new KeyValuePair<string, string>("f6","f6-upsert")
};
var fields = new List<KeyValuePair<string, string>>() {
    new KeyValuePair<string, string>("f3","f3-fields"),
    new KeyValuePair<string, string>("f4","f4-fields"),
    new KeyValuePair<string, string>("f6","f6-fields")
};
var server = new List<KeyValuePair<string, string>>() {
    new KeyValuePair<string, string>("f1","f1-server"),
    new KeyValuePair<string, string>("f2","f2-server"),
    new KeyValuePair<string, string>("f5","f5-server")
};

// Order of importance: Upsert > Fields > Server !

var stage = upsert.Concat(fields.Where(f=> !upsert.Any(u=>u.Key==f.Key)));
var final = stage.Concat(server.Where(s=> !stage.Any(j=>j.Key==s.Key))).OrderBy(o=>o.Key);

final.Dump();

LINQPad output:

Key | Value
------------
f1  | f1-upsert 
f2  | f2-server 
f3  | f3-fields 
f4  | f4-fields 
f5  | f5-server 
f6  | f6-upsert 
like image 862
Olby Avatar asked Jan 02 '23 19:01

Olby


2 Answers

This may or may not be what you're looking for, but personally I find that LINQ pretty difficult to read.

Here is a method which will duplicate your logic on as many collections as you'd like:

public List<KeyValuePair<string, string>> CombineWithPriority(params List<KeyValuePair<string, string>>[] allLists)
 {
     var results = new Dictionary<string, string>();

     foreach (var list in allLists)
     {
         foreach (var kvp in list)
         {
             if (!results.ContainsKey(kvp.Key))
             {
                 results.Add(kvp.Key, kvp.Value);
             }
         }
     }

     return results
         .OrderBy(kvp => kvp.Key)
         .ToList();
 }

To call it: CombineWithPriority(upsert, fields, server). You can add more levels as well, all with descending priority.

The biggest difference (besides readability, in my opinion) between this method and yours is that this method doesn't allocate temporary lists.

like image 175
johnnyRose Avatar answered Jan 05 '23 08:01

johnnyRose


You can use a GroupBy and select only the first value:

upsert.Concat(fields).Concat(server)
    .GroupBy(x => x.Key, (k, g) => g.First())
    .OrderBy(x => x.Key)
    .Dump();
like image 21
Xiaoy312 Avatar answered Jan 05 '23 08:01

Xiaoy312