I have 2 dictionaries that contain employee information form 2 DB tables in SAP Business 1. They have employee IDs and salaries e.g. I have ensured that the employee IDs of the 1st and 2nd table will always be the same
Table 1 (OHEM)
empID salary
1 40000
2 56000
3 77000
4 80000 <------increase
Table 2 (Salary Fitment)
empID salary
1 40000
2 56000
3 77000
4 50000
In the above example, if employee number 4 gets an increase/decrease (or any other employee salary change in OHEM), I would like to compare two dictionaries and then update
the corresponding salary in table two.
Code
// Get service instances
var employeeService = Program.Kernel.Get<IEmployeeService>();
var salaryFitmentService = Program.Kernel.Get<ISalaryFitmentService>();
var OHEMDictionary = employeeService.GetAllEmployees().OrderBy(es => es.empID)
.ToDictionary(od => od.empID,
od => od.salary);
var SalaryFitmentDictionary = salaryFitmentService.GetAllSalaryFitments().Where(x => x.U_PD_Code.Trim().ToString() == "SYS001").OrderBy(es => es.U_Employee_ID)
.ToDictionary(od => od.U_Employee_ID,
od => od.U_PD_Amount);
I already have an update code. What would be the best way to get the dictionary differences so that I could update the differences?
Something like this?
var diff = SalaryFitmentDictionary.Where(kv=>OHEMDictionary[kv.Key]!=kv.Value)
EDIT
You can also append to get the difference for each employee
.Select(kv => new { ID = kv.Key, Amount = OHEMDictionary[kv.Key] - kv.Value })
This is a straight forward but handy function:
private static Dictionary<string, string> DiffDictionary(Dictionary<string, string> first, Dictionary<string, string> second)
{
var diff = first.ToDictionary(e => e.Key, e => "removed");
foreach (var other in second)
{
string firstValue;
if (first.TryGetValue(other.Key, out firstValue))
{
diff[other.Key] = firstValue.Equals(other.Value) ? "same" : "different";
}
else
{
diff[other.Key] = "added";
}
}
return diff;
}
Especially if you just want to report the type of change:
var first = new Dictionary<string, string>() { { "one", "two" }, { "three", "four" }, { "five", "six" } };
var second = new Dictionary<string, string>() { { "one", "2" }, { "five", "six" }, { "seven", "eight" } };
foreach (var entry in DiffDictionary(first, second))
{
Console.WriteLine("{0} {1} ", entry.Key, entry.Value);
}
Which gives
one different
three removed
five same
seven added
There's not enough information to cover all bases (for example: are the keys in one dictionary a strict subset of those on the other? or maybe both dictionaries have exactly the same number and values of keys?), but generally this is what we 're talking about:
foreach(var pair in SalaryFitmentDictionary)
{
if(OHEMDictionary[pair.Key] != pair.Value)
{
// This employee's salary has changed
OHEMDictionary[pair.Key] = pair.Value;
}
}
Create a class to contain the differences:
public class DictionaryDifference<TKey, TValue>
{
public TKey Key
{
get;
set;
}
public TValue OriginalValue
{
get;
set;
}
public TValue NewValue
{
get;
set;
}
}
Create an extension method to find the differences:
public static class DictionaryExtensions
{
public static IEnumerable<DictionaryDifference<TKey, TValue>> GetDifferencesFrom<TKey, TValue>(
this IDictionary<TKey, TValue> original,
IDictionary<TKey, TValue> latest)
where TValue : IComparable
{
foreach (var originalItem in original)
{
if (latest.ContainsKey(originalItem.Key))
{
if (originalItem.Value.CompareTo(latest[originalItem.Key]) != 0)
{
// The key is in the latest but the value is different.
yield return new DictionaryDifference<TKey, TValue>
{
Key = originalItem.Key,
OriginalValue = originalItem.Value,
NewValue = latest[originalItem.Key]
};
}
}
else
{
// The key is not in the latest dictionary.
yield return new DictionaryDifference<TKey, TValue>
{
Key = originalItem.Key,
OriginalValue = originalItem.Value,
NewValue = default(TValue)
};
}
}
foreach (var newItem in latest)
{
if (!original.ContainsKey(newItem.Key))
{
// The key is not in the original dictionary.
yield return new DictionaryDifference<TKey, TValue>
{
Key = newItem.Key,
OriginalValue = default(TValue),
NewValue = latest[newItem.Key]
};
}
}
}
}
Create 2 dictionaries and compare:
var dictionary1 = new Dictionary<int, double>();
dictionary1.Add(1, 40000);
dictionary1.Add(2, 56000);
dictionary1.Add(3, 77000);
dictionary1.Add(4, 80000);
dictionary1.Add(5, 100000);
var dictionary2 = new Dictionary<int, double>();
dictionary2.Add(1, 40000);
dictionary2.Add(2, 56000);
dictionary2.Add(3, 77000);
dictionary2.Add(4, 50000);
dictionary2.Add(6, 35000);
foreach (var difference in dictionary1.GetDifferencesFrom(dictionary2))
{
Console.WriteLine(
"Key {0} was {1} but is now {2}",
difference.Key.ToString(),
difference.OriginalValue.ToString(),
difference.NewValue.ToString());
}
OUTPUT:
Key 4 was 80000 but is now 50000
Key 5 was 100000 but is now 0
Key 6 was 0 but is now 35000
var result = from o in OHEMDictionary
join f in SalaryFitmentDictionary on o.Key equals f.Key
where o.Value != f.Value
select { o.Key, o.Value - f.Value};
This will only cover those items that appear in both dictionaries, but you could do something like this:
Dictionary<int, string> first = new Dictionary<int, string>
{ { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
Dictionary<int, string> second = new Dictionary<int, string>
{ { 1, "One" }, { 2, "Two" }, { 3, "Tri" } };
var difference = from f in first
join s in second on f.Key equals s.Key
where f.Value != s.Value
select new {Key = f.Key,
FirstValue = f.Value,
SecondValue = s.Value };
foreach (var item in difference)
{
Console.WriteLine("Different item with key {0}, Values {1} and {2}",
item.Key, item.FirstValue, item.SecondValue);
}
Related
I am having difficulty combining two dictionaries into a dictionary with two values combined to a list for identical keys. For example, having D1 and D2
D1 = {2:"a",
3:"b",
4: "c"}
D2 = {1:"e",
2:"f",
4:"h",
5:"i"}
I would like to create D3.
D3= { 1:["", "e"]
2:["a", "f"]
3:["b", ""]
4:["c":"h"]
5:["", "i"]}
Thank you.
This can be done in a single Linq expression like so:
Flatten and concatenate both d1 and d2 to a single flat sequence of (Int32,String) value-tuples.
Re-group them by the Int32 key (this is the main step).
Then convert each group into a separate output dictionary entry.
Dictionary<Int32,String> d1 = new Dictionary<Int32,String>()
{
{ 2, "a" },
{ 3, "b" },
{ 4, "c" },
};
Dictionary<Int32,String> d2 = new Dictionary<Int32,String>()
{
{ 1, "e" },
{ 2, "f" },
{ 4, "h" },
{ 5, "i" },
};
Dictionary<Int32,List<String>> d3 = Array
.Empty<( Int32 k, String v )>()
// Step 1:
.Concat( d1.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
.Concat( d2.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
// Step 2:
.GroupBy( t => t.k )
// Step 3:
.ToDictionary(
grp => grp.Key,
grp => grp.Select( t => t.v ).OrderBy( v => v ).ToList()
);
An advantage of this approach is that it works for any number of duplicated values (not just two). Also, the use of ValueTuple means this approach should have fewer heap-allocations.
Screenshot proof of it working in LinqPad:
The expression can be made more succint - I use a more verbose style myself, but if you want to be cryptic about it by re-using KeyValuePair instead of ValueTuple, and if you don't care about ordering, then you can do this:
var d3 = d1
.Concat( d2 )
.GroupBy( kvp => kvp.Key )
.ToDictionary( g => g.Key, g => g.Select( kvp => kvp.Value ).ToList() );
Simplest solution would be with Dictionary.Keys
var D1 = new Dictionary<int,string>(){{2,"a"}, {3,"b"},{4,"c"}};
var D2 = new Dictionary<int,string>(){{1,"e"},{2,"f"}, {4,"h"},{5,"i"}};
var keys = D1.Keys.Union(D2.Keys).OrderBy(key => key);
var test = keys.Select(key => new {Key = key, Value= new string[] {D1.ContainsKey(key) ? D1[key] : "", D2.ContainsKey(key) ? D2[key] : ""} });
Console.WriteLine(test);
Interactive: https://rextester.com/UXQ51844
Alternatively, you could do something similar to this: LINQ - Full Outer Join
Sounds to be a job for LINQ. Here is one possibility to solve this issue:
public class Element
{
public int Index { get; set; }
public string Value { get; set; }
}
public class GroupedElement
{
public int Index { get; set; }
public IReadOnlyList<string> Values { get; set; }
}
public static class Program
{
public static void Main(string[] args)
{
var d1 = new[]
{
new Element { Index = 2, Value = "a" },
new Element { Index = 3, Value = "b" },
new Element { Index = 4, Value = "c" },
};
var d2 = new[]
{
new Element { Index = 1, Value = "e" },
new Element { Index = 2, Value = "f" },
new Element { Index = 4, Value = "h" },
new Element { Index = 5, Value = "i" },
};
var result = d1.Concat(d2)
.GroupBy(element => element.Index)
.Select(group => new GroupedElement { Index = group.Key, Values = group.Select(g => g.Value).ToList() })
.ToList();
foreach (var item in result)
{
Console.WriteLine($"{item.Index}: {string.Join(",", item.Values)}");
}
}
}
I think this is simpler without LINQ (LINQ is a hammer, not every problem is a nail)
Let's loop from 1 to 5, putting a new List for each int. The list is inited with d1's value and d2's value
var d3 = new Dictionary<int, List<string>>();
for(int x=1;x<6;x++)
d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };
If your ints aren't always contiguous you could (use a bit of LINQ 😀 and..)
foreach(int x in d1.Keys.Union(d2.Keys))
d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };
This doesn't need another answer the others answers are plenty good enough and well done, however here is another (convoluted) approach
Given
var d1 = new Dictionary<int, string> {{2, "a"}, {3, "b"}, {4, "c"}};
var d2 = new Dictionary<int, string> { { 1, "e" }, { 2, "f" }, { 4, "h" }, { 5, "i" } };
Usage
static string[] Stuff((string, string)[] v) =>
new[] {v[0].Item1 ?? v.ElementAtOrDefault(1).Item1 ?? "", v[0].Item2 ?? v.ElementAtOrDefault(1).Item2 ?? "" };
var result = d1
.Select(x => (x.Key, (x.Value,(string)null)))
.Concat(d2.Select(x => (x.Key, ((string)null, x.Value ))))
.GroupBy(element => element.Key)
.ToDictionary(x => x.Key, x => Stuff(x.Select(y =>y.Item2).ToArray()))
Output
foreach (var item in result.OrderBy(x => x.Key))
Console.WriteLine($"{item.Key}: {string.Join(",", item.Value)}");
---
1: ,e
2: a,f
3: b,
4: c,h
5: ,i
Please see, here is another approach for this query -
Input -
Dictionary<int, string> first_dict = new Dictionary<int, string>()
{
{ 2,"a" },
{ 3,"b" },
{ 4, "c"}
};
Dictionary<int, string> second_dict = new Dictionary<int, string>()
{
{ 1,"e" },
{ 2,"f" },
{ 4, "h"},
{ 5, "i"}
};
First, I got common keys from both dictionaries like this -
var allKeys = first_dict.Concat(second_dict).OrderBy(b => b.Key).Select(b => b.Key).Distinct().ToList();
and then I created two another dictionaries and inserted data into them like this -
Dictionary<int, string> first_dict_res = new Dictionary<int, string>();
Dictionary<int, string> second_dict_res = new Dictionary<int, string>();
foreach (var keyItem in allKeys)
{
var first_dict_res_value = (first_dict.ContainsKey(keyItem)) ? first_dict[keyItem] : null;
first_dict_res.Add(keyItem, first_dict_res_value);
var second_dict_res_value = (second_dict.ContainsKey(keyItem)) ? second_dict[keyItem] : null;
second_dict_res.Add(keyItem, second_dict_res_value);
}
and then I concatenated the result from both dictionaries to get the desired result-
var res_dict = first_dict_res.Concat(second_dict_res).GroupBy(b => b.Key)
.Select(c => new { key = c.Key, values = string.Join(",", c.Select(b => b.Value)) }).ToList();
I have a list of employee work hours data by day. I want to pivot day and hours data and create a new list of object with employee name, and series of days with respective hours as value.
My list looks like
List<EmployeeHour> employeeHours = new List<EmployeeHour>
{
new EmployeeHour{Id = 1, EmployeeName = "Emp1", Day = 1, Hours = 6},
new EmployeeHour{Id = 1, EmployeeName = "Emp1", Day = 2, Hours = 8},
new EmployeeHour{Id = 2, EmployeeName = "Emp2", Day = 1, Hours = 6},
new EmployeeHour{Id = 2, EmployeeName = "Emp2", Day = 2, Hours = 8}
};
I want to create new list of the object where Hours dsomething like
new {
Id = 1,
EmplyeeName = Emp1,
Day1 = 6,
Day2 = 8
},
new {
Id = 2,
EmplyeeName = Emp2,
Day1 = 6,
Day2 = 8
}
This is how i did it. Is there better solution?
dynamic d = new
{
Employee = group.FirstOrDefault().EmployeeName,
Day1 = group.FirstOrDefault(g => g.DayNumber == 1)?.HoursPerDay,
Day2 = group.FirstOrDefault(g => g.DayNumber == 2)?.HoursPerDay,
Day3 = group.FirstOrDefault(g => g.DayNumber == 3)?.HoursPerDay,
Day4 = group.FirstOrDefault(g => g.DayNumber == 4)?.HoursPerDay,
Day5 = group.FirstOrDefault(g => g.DayNumber == 5)?.HoursPerDay,
Day6 = group.FirstOrDefault(g => g.DayNumber == 6)?.HoursPerDay,
Day7 = group.FirstOrDefault(g => g.DayNumber == 7)?.HoursPerDay
};
list.Add(d);
Use a dictionary.
C# is a strongly typed language so variability in class properties is limited. What appears to be better suited in your case is a Dictionary<string, object>:
var dictionaries = employeeHours.Select(x => new {x.Id, x.EmployeeName})
.Distinct()
.Select(x => new Dictionary<string, object>()
{
{"Id", x.Id},
{"EmployeeName", x.EmployeeName}
})
.ToList();
foreach (var dict in dictionaries)
{
var id = (int)dict["Id"];
foreach (var dh in employeeHours.Where(x => x.Id == id))
{
if (!dict.ContainsKey("Day" + dh.Day))
{
dict["Day" + dh.Day] = dh.Hours;
}
}
}
This code will give you a dictionary of variable lengths, and when this is transformed using JSON, it should give you the data structure you need in datatables.
If you want the result to be a statically typed anonymous (or defined class) object, you will need to do something like what you are doing.
I would use an enhanced dictionary type converted from each group to make it easier to extract the values:
//***
// Enhanced Dictionary that returns default(TValue) for missing values
//***
public class NullDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
public NullDictionary(IDictionary<TKey, TValue> d) : base() {
foreach (var kvp in d)
Add(kvp.Key, kvp.Value);
}
public NullDictionary() : base() { }
public new TValue this[TKey key]
{
get
{
TryGetValue(key, out var val);
return val;
}
set
{
base[key] = value;
}
}
}
With an extension method to make creation easy:
public static class DictExt {
public static NullDictionary<TKey, TValue> ToNullDictionary<T, TKey, TValue>(this IEnumerable<T> src, Func<T, TKey> keyFn, Func<T, TValue> valFn) {
var nd = new NullDictionary<TKey, TValue>();
foreach (var s in src)
nd.Add(keyFn(s), valFn(s));
return nd;
}
}
Now you can pivot the data using GroupBy and extract the anonymous objects:
var ans = employeeHours.GroupBy(eh => new { eh.Id, eh.EmployeeName }, eh => new { Day = $"Day{eh.Day}", eh.Hours })
.Select(pg => {
var pd = pg.ToNullDictionary(p => p.Day, p => (int?)p.Hours);
return new {
pg.Key.Id,
pg.Key.EmployeeName,
Day1 = pd["Day1"],
Day2 = pd["Day2"],
Day3 = pd["Day3"],
Day4 = pd["Day4"],
Day5 = pd["Day5"],
Day6 = pd["Day6"],
Day7 = pd["Day7"],
};
})
.ToList();
It is also possible to use a more dynamic object, such as a DataTable, or a Dictionary or ExpandoObject for each list member, or even to create an anonymous object at runtime, though that is of questionable value most of the time since you can't easily access its fields. However, if you did not know how may Day values may be possible, and want to handle a varying number, you must use one of these or replace the day fields with a collection of some sort instead (e.g. an array, list, or dictionary).
In a C# List>, how can I extract unique key/value pairs and store it in List>?
List<Dictionary<string,string>> md = new List<Dictionary<string,string>>();
Input
md[0] :
[0]:"A","Apple"
[1]:"B","Ball"
md[1]:
[0]:"A","Apple"
[1]:"B","Ball"
md[2]:
[0]: "C", "Cat"
[1]: "D", "Dog"
Output
md[0] :
[0]:"A","Apple"
[1]:"B","Ball"
md[1]:
[0]:"C" : "Cat"
[1]:"D" : "Dog"
Code sample to extract both unique key/value pairs are needed, only unique keys or unique values are not needed.
(* Note : [0],[1] above depicts the indexes in the list and dictionary and not the keys or values)
List<Dictionary<string,string>> md = new List<Dictionary<string,string>>();
var unique = new Dictionary<string, string>();
foreach (var m in md)
{
foreach(var innerKey in m.Key)
{
if (!unique.ContainsKey(innerKey))
{
unique.Add(innerKey, m[innerKey]);
}
}
}
One possible strictly correct solution would be to implement IEqualityComparer<Dictionary<string, string>>:
public class DictionaryComparer : IEqualityComparer<Dictionary<string, string>>
{
public int GetHashCode(Dictionary<string, string> d)
{
var hashCode = 17;
foreach (var entry in d.OrderBy(kvp => kvp.Key))
{
hashCode = hashCode * 23 + entry.Key.GetHashCode();
hashCode = hashCode * 23 + entry.Value.GetHashCode();
}
return hashCode;
}
public bool Equals(Dictionary<string, string> d1, Dictionary<string, string> d2)
{
string value2;
return d1.Count == d2.Count && d1.All(kvp => d2.TryGetValue(kvp.Key, out value2) && kvp.Value == value2);
}
}
Then to get your list of unique dictionaries:
var result = md.Distinct(new DictionaryComparer()).ToList();
You can do it with linq.
List<Dictionary<string, string>> md = new List<Dictionary<string, string>>();
md.Add(new Dictionary<string, string>() { { "A","Apple"}, { "B", "Ball" } });
md.Add(new Dictionary<string, string>() { { "A","Apple"}, { "B", "Ball" } });
md.Add(new Dictionary<string, string>() { { "C","Cat"}, { "D", "Dog" } });
var filtered =
md.GroupBy(x => string.Join("", x.Select(i => string.Format("{0}{1}", i.Key, i.Value)))).Select(x => x.First());
Variable "filtered" contains list of dictionaries with unique sets.
I have the two lists:
List<string> keys = new List<string>()
{
"REPORTMONTH",
"CONTRACT", "DATE", "AMOUNT",
"CONTRACT", "DATE", "AMOUNT"
};
List<string> values = new List<string>()
{
"01",
"ABC123", "01022014", "300.00",
"DEF345", "03042014", "400.00"
};
The first list represents keywords which can have certain repetitions. The second list contains values associated with the keys in the first list (by index). The result output should be of type List<Dictionary<string, string>> and contain:
1st dictionary
key value
"REPORTMONTH" "01"
"CONTRACT" "ABC123"
"DATE" "01022014"
"AMOUNT" "300.00"
2nd dictionary
key value
"REPORTMONTH" "01"
"CONTRACT" "DEF345"
"DATE" "03042014"
"AMOUNT" "400.00"
I.e. the keys that do not repeat should present in both dictionaries, the rest should be splitted into dictionaries with associated values.
Note, there can be no repeptitions at all, or more than 2.
Sorry, I cannot show my attempts for this question, because I'm not sure how to start. Maybe using LINQ and grouping can solve the problem?
Thank you.
You could do this in a way that does not look that elegant, requiring some loops. Note that this will also work if there are more than 2 dictionaries.
public static void Main(params string[] args)
{
List<string> keys = new List<string>() {
"REPORTMONTH",
"CONTRACT", "DATE", "AMOUNT",
"CONTRACT", "DATE", "AMOUNT"
};
List<string> values = new List<string>() {
"01",
"ABC123", "01022014", "300.00",
"DEF345", "03042014", "400.00"
};
var pairs = keys.Select((key, ndx) => new { Key = key, Value = values[ndx] });
var groups = pairs.GroupBy(e => e.Key)
.ToDictionary(g => g.Key, g => g.Select(kvp => kvp.Value).ToArray());
var dictionaries = new Dictionary<string, string>[groups.Max(g => g.Value.Length)];
for (var i = 0; i < dictionaries.Length; i++)
{
dictionaries[i] = new Dictionary<string,string>();
foreach (var g in groups)
{
if (g.Value.Length == 1)
dictionaries[i][g.Key] = g.Value[0];
else if (g.Value.Length > i)
dictionaries[i][g.Key] = g.Value[i];
}
}
// print content
for (var i = 0; i < dictionaries.Length; i++)
{
Console.WriteLine("Dictionary {0}:", i + 1);
Console.WriteLine(string.Join(Environment.NewLine, dictionaries[i].Select(e => string.Format("{0} = {1}", e.Key, e.Value))));
Console.WriteLine();
}
Console.ReadLine();
}
You can do this by first using Enumerable.Zip() to get a sequence of key/value pairs and convert the list into a lookup, then process that list into two dictionaries:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
List<string> keys = new List<string>
{
"REPORTMONTH",
"CONTRACT", "DATE", "AMOUNT",
"CONTRACT", "DATE", "AMOUNT"
};
List<string> values = new List<string>
{
"01",
"ABC123", "01022014", "300.00",
"DEF345", "03042014", "400.00"
};
var combined = Enumerable.Zip(
keys, values, (key, value) => new { Key = key, Value = value})
.ToLookup(entry => entry.Key);
var dicts = new []
{
new Dictionary<string, string>(),
new Dictionary<string, string>()
};
foreach (var items in combined)
{
int count = 0;
string lastKey = null;
foreach (var item in items.Take(2))
{
dicts[count++][item.Key] = item.Value;
lastKey = item.Key;
}
if (count == 1)
dicts[1][lastKey] = dicts[0][lastKey];
}
dump("1st dictionary", dicts[0]);
dump("2nd dictionary", dicts[1]);
}
static void dump(string title, Dictionary<string, string> data)
{
Console.WriteLine(title);
foreach (var item in data)
Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
Console.WriteLine();
}
}
}
I have two Dictionary<string, Item>s. Item has a has a public property int Level.
I want to combine these two dictionaries where keys are unique and I want to be able to specify level on both.
Something like
dictionary 1 = all items that level < 10
dictionary 2 = all items level < 20
combine dictionary 2 with 1 (where Value.Level < 10)
if the Key is unique and the Value.Level < 20
I can easily do this with foreach loops. I can also do this with multiple linq queries.
However i can't seem to figure out how to make this one single linq query.
Edit- Per your request John here is the code with foreach
Dictionary<string, Dictionary<string, Item>> itemDictionary = new Dictionary<string, Dictionary<string, Item>>();
Dictionary<string, Item> items = new Dictionary<string,Item>();
if (itemDictionary.ContainsKey(comboBox.Text))
{
foreach (KeyValuePair<string, Item> kvp in itemDictionary[comboBox.Text])
{
if (!items.ContainsKey(kvp.Key) && kvp.Value.Level <= int.Parse(textBox.Text))
items.Add(kvp.Key, kvp.Value);
}
}
if (itemDictionary.ContainsKey(comboBox1.Text))
{
foreach (KeyValuePair<string, Spell> kvp in itemDictionary[comboBox1.Text])
{
if (!items.ContainsKey(kvp.Key) && kvp.Value.Level <= int.Parse(textBox1.Text))
items.Add(kvp.Key, kvp.Value);
}
}
var query = from s in items
orderby s.Value.Level
select s;
foreach (var i in query)
listBox.Items.Add(string.Format("{0} {1}", i.Value.Level, i.Key));
If you can easily do what you want in foreach loops, then you've already written the code. That tells me that the foreach code would probably be more readable and easier to maintain than a complicated LINQ query.
This sounds like the perfect case for leaving foreach loop code in place. LINQ may be newer but that doesn't always mean it is better.
Ok, that code makes it clear what you want to accomplish. So the end result should be a dictionary comprised of the items from both dictionaries that meet the specified level. If an item exists in both dictionaries than the item from the first dictionary will be preferred. While it is possible to accomplish this in a single Linq query you would end up repeating some work. Here is what I came up with, it runs in LinqPad if you want to try it out easily.
var itemsOne = new[] {
new { Name = "A", Level = 1 },
new { Name = "B", Level = 2 },
new { Name = "C", Level = 3 },
new { Name = "D", Level = 4 }
}.ToDictionary(i => i.Name, i => i);
var itemsTwo = new[] {
new { Name = "C", Level = 10 },
new { Name = "D", Level = 20 },
new { Name = "E", Level = 30 },
new { Name = "F", Level = 40 }
}.ToDictionary(i => i.Name, i => i);
var itemsOneLevel = 3;
var itemsTwoLevel = 30;
var validFromItemsOne = (from item in itemsOne
where item.Value.Level <= itemsOneLevel
select item).ToDictionary(i => i.Key, i => i.Value);
var validFromItemsTwo = from item in itemsTwo
where item.Value.Level <= itemsTwoLevel
&& !validFromItemsOne.ContainsKey(item.Key)
select item;
var items = validFromItemsOne
.Concat(validFromItemsTwo)
.ToDictionary(i => i.Key, i => i.Value);
If I undestood you correct: you want to have records that exist in both dictionaries. Anyway my example can be tailored to your particular needs.
Dictionary<string, string> d1 = new Dictionary<string, string>(), d2 = new Dictionary<string, string>();
var merged = d1
.Join(d2, d1key => d1key.Key, d2key => d2key.Key,
(i1, i2) => new { Key = i1.Key, Value1 = i1.Value, Value2 = i2.Value })
.ToDictionary(t => t.Key);
Merged will contain only items that existed in both dictionaries, and will be able to get data for each of the items like merged["key"].Value1 and merged["key"].Value2
For example I`ve used anonymous type (new {}). So you will be able to access Value1 and Value2 only in the same method, or in binding because they are dynamic anyway.
In other cases you should create a type to store combined result.
Here is an extension that I think will work for this scenario.
public static class Extensions
{
public static IDictionary<TKey, TValue> Combine<TKey, TValue>(
this IDictionary<TKey, TValue> firstSet,
IDictionary<TKey, TValue> secondSet,
Func<KeyValuePair<TKey, TValue>, bool> firstFilter,
Func<KeyValuePair<TKey, TValue>, bool> secondFilter)
{
return firstSet
.Where(firstFilter)
.Concat(secondSet.Where(secondFilter))
.GroupBy(d => d.Key)
.ToDictionary(d => d.Key, d => d.First().Value);
}
}
Usage:
var itemsOne = new[] {
new { Name = "A", Level = 1 },
new { Name = "B", Level = 2 },
new { Name = "C", Level = 3 },
new { Name = "D", Level = 4 }
}.ToDictionary(i => i.Name, i => i);
var itemsTwo = new[] {
new { Name = "C", Level = 10 },
new { Name = "D", Level = 20 },
new { Name = "E", Level = 30 },
new { Name = "F", Level = 40 }
}.ToDictionary(i => i.Name, i => i);
itemsOne
.Combine(itemsTwo,
kvp => kvp.Value.Level <= 3,
kvp => kvp.Value.Level <= 30)
.ToDictionary(d => d.Key, d=> d.Value.Level)
Result:
A 1
B 2
C 3
D 20
E 30