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();
}
}
}
Related
With a sorted list:
[11] = "a"
[22] = "b"
[35] = "c"
[40] = "d"
[45] = "e"
and a list of keys:
[35, 40, 45]
how can I get the data matching the list of keys.
the output should be:
["c", "d", "e"]
Edit:
The type is SortedList()
the class 'SomeClass' contains the key value as well.
an example would be:
class SomeClass
{
string Key;
... some other fields
}
my attempt was:
MyList.Values.Where(_ => keys.Contains(_.key)).ToList();
but this is not using the index.
This works as long as all elements can be found:
public class A
{
public string Key;
public string SomeValue;
}
var l = new SortedList<string, A>
{
["aa"] = new A { Key = "aa" },
["bb"] = new A { Key = "bb" },
["cc"] = new A { Key = "cc" },
["dd"] = new A { Key = "dd" }
};
var ids = new List<string> { "bb", "cc" };
var r = ids.Select(i => l[i]).ToList();
Following the advice of using TryGetValue is good, but I'm not sure how to combine it with select.
Depending on the size of the SortedList and the size of ids, the below code maybe worth considering (to avoid iterating over the entirety of SortedList). It will be particularly beneficial if ids is small and list is large.
using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApp12
{
public class Program
{
public static void Main(string[] args)
{
var list = new SortedList<string, SomeClass>
{
["aa"] = new SomeClass { Key = "aa" },
["bb"] = new SomeClass { Key = "bb" },
["cc"] = new SomeClass { Key = "cc" },
["dd"] = new SomeClass { Key = "dd" }
};
var ids = new List<string> { "bb", "cc" };
var results = ids.Select(x =>
{
list.TryGetValue(x, out var matching);
return matching;
}).Where(z => z != null);
// Output to show the results
Console.WriteLine(string.Join(",", results.Select(z => z.Key)));
}
}
}
So, this will work?
var list = new SortedList<string, SomeClass>
{
["aa"] = new SomeClass { Key = "aa" },
["bb"] = new SomeClass { Key = "bb" },
["cc"] = new SomeClass { Key = "cc" },
["dd"] = new SomeClass { Key = "dd" }
};
var ids = new List<string> { "bb", "cc" };
var results = list.Where(x => ids.Contains(x.Key)).ToList();
If you need only the SomeClass list:
var someClassList = list.Where(x => ids.Contains(x.Key)).Select(y => y.Value);
Below is the code where key is being hard-coded in Dictionary
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ "ID", Convert.ToString(dt.Rows[i]["ID"]) },
{ "STATUS", Convert.ToString(dt.Rows[i]["Name"]) },
{ "TYPE", Convert.ToString(dt.Rows[i]["TYPE"]) }
};
datalist.Add(data);
}
Now, instead of hard-coding the keys like ID, STATUS, etc, I want to add it from my string array containing the values below
string[] arrNames = ConfigurationManager.AppSettings["NameKey"].Split(',');
How can I traverse arrNamesto add keys in Dictionary and then add in List?
Iterate through the collection of names:
var datalist = new List<IDictionary<string, string>>();
string[] arrNames = ConfigurationManager.AppSettings["NameKey"].Split(',');
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>();
foreach (var name in arrNames)
{
data[name] = Convert.ToString(dt.Rows[i][name]);
}
datalist.Add(data);
}
your code should look something like this
var datalist = new List<IDictionary<string, string>>();
string[] arrNames = Convert.ToString(ConfigurationManager.AppSettings["NameKey"]).Split(',');
if (arrNames.Length == 3)
{
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ arrNames[0], Convert.ToString(dt.Rows[i][arrNames[0]]) },
{ arrNames[1], Convert.ToString(dt.Rows[i][arrNames[1]]) },
{ arrNames[2], Convert.ToString(dt.Rows[i][arrNames[2]]) }
};
datalist.Add(data);
}
}
You can use linq method ToDictionary. Try this code:
string[] arrNames = // new[] {"ID", "STATUS", "TYPE"};
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
datalist.Add(
arrNames
.Select(key =>
new
{
key,
value = Convert.ToString(dt.Rows[i][key])
}
)
.ToDictionary(x => x.key, x => x.value)
);
If you prefer LINQ-y and concise you could try something like:
var names = ConfigurationManager.AppSettings["NameKey"].Split(',');
var list = dt.AsEnumerable()
.Select(r => names.ToDictionary(n => n, n => r[n]))
.ToList();
Here I'm assuming dt is a DataTable.
If you have at least the same number of items in your arrNames array than columns you want to read and of course with this order, then you can hardcore the indexes.
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dt.Rows.Count; ++i)
{
var data = new Dictionary<string, string>()
{
{ arrNames[0], Convert.ToString(dt.Rows[i]["ID"]) },
{ arrNames[1], Convert.ToString(dt.Rows[i]["Name"]) },
{ arrNames[2], Convert.ToString(dt.Rows[i]["TYPE"]) }
};
datalist.Add(data);
}
I have a Dictionary that looks like the following, with the key being an Integer and the value being a List of strings:
var x = new Dictionary<int, List<string>>;
I would like to see if any of those Lists match each other (without being in order) so that I can group them together in a role.
The final solution will look like
var y = new Dictionary<string, List<int>>
Where the List<int> is the keys from var x. The string key will be a machine generated string such as a guid, etc.
You can map all values to their keys and then group them by value and then apply ToDictionary, for expected result.
var data = new Dictionary<int, List<string>>
{
{ 1, new List<string> { "Adam", "Lucie" } },
{ 2, new List<string> { "Adam", "Hannah" } },
{ 3, new List<string> { "John", "Rachel" } },
{ 4, new List<string> { "Bill", "Hannah" } },
};
var result = data.SelectMany(p => p.Value.Select(v => new {Key = p.Key, Value = v}))
.GroupBy(o => o.Value)
.ToDictionary(g => g.Key, g => g.Select(v => v.Key));
foreach (var keyValues in result)
{
Console.WriteLine(keyValues.Key + ": " + string.Join(", ", keyValues.Value));
}
I have this list:
var items = new List<string>() { "Hello", "I am a value", "Bye" };
I want it to convert it to a dictionary with the following structure:
var dic = new Dictionary<int, string>()
{
{ 1, "Hello" },
{ 2, "I am a value" },
{ 3, "Bye" }
};
As you can see, the dictionary keys are just incremental values, but they should also reflect the positions of each element in the list.
I am looking for a one-line LINQ statement. Something like this:
var dic = items.ToDictionary(i => **Specify incremental key or get element index**, i => i);
You can do that by using the overload of Enumerable.Select which passes the index of the element:
var dic = items.Select((val, index) => new { Index = index, Value = val})
.ToDictionary(i => i.Index, i => i.Value);
static void Main(string[] args)
{
var items = new List<string>() { "Hello", "I am a value", "Bye" };
int i = 1;
var dict = items.ToDictionary(A => i++, A => A);
foreach (var v in dict)
{
Console.WriteLine(v.Key + " " + v.Value);
}
Console.ReadLine();
}
Output
1 Hello
2 I am a value
3 Bye
EDIT: Out of curosity i did a performance test with a list of 3 million strings.
1st Place: Simple For loop to add items to a dictionary using the loop count as the key value. (Time: 00:00:00.2494029)
2nd Place: This answer using a integer variable outside of LINQ. Time(00:00:00.2931745)
3rd Place: Yuval Itzchakov's Answer doing it all on a single line. Time (00:00:00.7308006)
var items = new List<string>() { "Hello", "I am a value", "Bye" };
solution #1:
var dic2 = items.Select((item, index) => new { index, item })
.ToDictionary(x => x.item, x => x.index);
solution #2:
int counter = 0;
var dic = items.ToDictionary(x => x, z => counter++);
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);
}