Olby
Olby

Reputation: 78

LINQ - Query to combine 3 datasets - improvements

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 

Upvotes: 4

Views: 82

Answers (2)

johnnyRose
johnnyRose

Reputation: 7490

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.

Upvotes: 5

Xiaoy312
Xiaoy312

Reputation: 14477

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();

Upvotes: 4

Related Questions