Can the new feature in C# 7.0 (in VS 2017) to give tuple fields names be translated to KeyValuePairs?
Lets assume I have this:
class Entry
{
public string SomeProperty { get; set; }
}
var allEntries = new Dictionary<int, List<Entry>>();
// adding some keys with some lists of Entry
It would be nice to do something like:
foreach ((int collectionId, List<Entry> entries) in allEntries)
I have already added System.ValueTuple to the project.
Being able to write it like that would be much better than this traditional style:
foreach (var kvp in allEntries)
{
int collectionId = kvp.Key;
List<Entry> entries = kvp.Value;
}
Deconstruction requires a Deconstruct method defined either on the type itself, or as an extension method. KeyValuePaire<K,V> itself doesn't have a Deconstruct method, so you need to define an extension method:
static class MyExtensions
{
public static void Deconstruct<K,V>(this KeyValuePair<K,V> kvp, out K key, out V value)
{
key=kvp.Key;
value=kvp.Value;
}
}
This allows you to write:
var allEntries = new Dictionary<int, List<Entry>>();
foreach(var (key, entries) in allEntries)
{
...
}
For example:
var allEntries = new Dictionary<int, List<Entry>>{
[5]=new List<Entry>{
new Entry{SomeProperty="sdf"},
new Entry{SomeProperty="sdasdf"}
},
[11]=new List<Entry>{
new Entry{SomeProperty="sdfasd"},
new Entry{SomeProperty="sdasdfasdf"}
}, };
foreach(var (key, entries) in allEntries)
{
Console.WriteLine(key);
foreach(var entry in entries)
{
Console.WriteLine($"\t{entry.SomeProperty}");
}
}
Related
I have a legacy class that looks like this:
public class LegacyBusinessObject
{
....(100 similar fields in total)
public Dictionary<string, string> SomeBusinessValue1 = new Dictionary<string, string>();
public Dictionary<string, long> SomeBusinessValue2 = new Dictionary<string, long>();
public Dictionary<string, decimal> SomeBusinessValue3 = new Dictionary<string, decimal>();
....
}
whereas the string key denominates the provider this value came from.
So for context: "SomeBusinessValue1" could be a weight measurement, that differs depending on the lab that did it.
I want to merge several of these monsters into one object using reflection:
public LegacyBusinessObject Merge(Dictionary<string, LegacyBusinessObject> objects)
{
var result = new LegacyBusinessObject();
//Loop through all the business object's fields
foreach (var prop in typeof(LegacyBusinessObject).GetFields())
{
//Second loop through all the individual objects from different providers
foreach (var ep in objects)
{
//Here I would need to test for all possivle value types that could
//be in the dictionary: <string, string>, <string, long>...
//then cast to it and invoke the Add method like this:
var propDictionary = prop.GetValue(result) as Dictionary<string, string>;
propDictionary.Add(ep.Key, ep.Value);
}
}
return result;
}
Now this approach requires me to do a lot of clumsy casts for propDictionary. (I also tried consctructing the matching keyvaluepair<,> and an Activator to instance it; but i can't find a way to add this to another dictionary)
Can you think of a better way to perform this merge, that takes arbitrary dictionary value types?
Some more context:
I am getting a LegacyBusinessObject Obj1 with data from Lab A and Lab B that is stored in the dictionaries. No I am cleaning up the database and find out that another LegacyBusinessObject Obj2 has Data from Lab C and Lab D. As it turns out there was a mistake during ingestion and Obj1 and Obj2 are for the same product and have wrongfully been stored in two different LegacyBusinessObjects. I now want to merge the data to get a new LegacyBusinessObject with Data from Lab A through D
Quite unclear what you are exactly asking, but:
public static LegacyBusinessObject Merge(Dictionary<string, LegacyBusinessObject> objects)
{
var result = new LegacyBusinessObject();
foreach (var prop in typeof(LegacyBusinessObject).GetFields())
{
var propDictionaryNew = (IDictionary)prop.GetValue(result);
foreach (var dict in objects)
{
var propDictionaryOld = (IDictionary)prop.GetValue(dict.Value);
foreach (DictionaryEntry de in propDictionaryOld)
{
propDictionaryNew[de.Key] = de.Value;
// Or:
//((IDictionary)result).Add(de.Key, de.Value);
// But be aware of exceptions if de.Key is present in multiple dictionaries
}
}
}
return result;
}
and then, to test it:
var lbo1 = new LegacyBusinessObject
{
SomeBusinessValue1 = new Dictionary<string, string> { { "A1", "A2" }, { "B1", "B2" } },
SomeBusinessValue2 = new Dictionary<string, long> { { "C1", 1 }, { "D1", 2 } },
SomeBusinessValue3 = new Dictionary<string, decimal> { { "E1", 3 }, { "F1", 4 } },
};
var lbo2 = new LegacyBusinessObject
{
SomeBusinessValue1 = new Dictionary<string, string> { { "G1", "G2" }, { "H1", "H2" } },
SomeBusinessValue2 = new Dictionary<string, long> { { "I1", 5 }, { "J1", 6 } },
SomeBusinessValue3 = new Dictionary<string, decimal> { { "K1", 7 }, { "L1", 8 } },
};
var result = Merge(new Dictionary<string, LegacyBusinessObject> { { "X", lbo1 }, { "Y", lbo2 } });
I'm cheating a little here... Dictionary<,> implements the pre-generics interface IDictionary (that is different from IDictionary<,>) that uses object as key and value. In this way I don't have to support the different value types. When using reflection with generic collections, a good trick is to see if the non-generic interfaces are enough to do what you need (because they are much easier to handle with reflection).
I have two list object A and B like this:
A=[
{item:1, value:"ex1"},
{item:2, value:"ex1"},
{item:3, value:"ex1"},
{item:4, value:"ex1"}]
B=[{item:1, value:"ex2"},
{item:4, value:"ex2"},
{item:2, value:"ex3"},
{item:5, value:"ex3"}]
How can i do to make B to have same items/values like A and still keep its own sequence for items it already has?
I want B to remove item 5 because A don't have it, and add in item 3 to the end.
I don't want to clone A, I want to modify B to become like A.
What I want is:
Remove items in B when A don't have them: item5
Update items in B when both A and B have them: item1, item2, item4
Add non-existing items to end of B when A have them: item3
So, the result should be like this:
B = [ {item:1, value:"ex1"},
{item:4, value:"ex1"},
{item:2, value:"ex1"},
{item:3, value:"ex1"} ]
Mycode: (This is what i have now)
foreach (myClass itm in A)
{
foreach (myClass fd in B)
{
if (itm.item == fd.item)
{
fd.value = itm.value;
}
}
}
You can write an extension method that merges the lists by iterating over the keys and checking for existence.
static class ExtensionMethods
{
static public void MergeInto<TKey,TValue>(this Dictionary<TKey,TValue> rhs, Dictionary<TKey,TValue> lhs)
{
foreach (var key in rhs.Keys.Union(lhs.Keys).Distinct().ToList())
{
if (!rhs.ContainsKey(key))
{
lhs.Remove(key);
continue;
}
if (!lhs.ContainsKey(key))
{
lhs.Add(key, rhs[key]);
continue;
}
lhs[key] = rhs[key];
}
}
}
Test program:
public class Program
{
public static Dictionary<int,string> A = new Dictionary<int,string>
{
{ 1,"ex1" },
{ 2,"EX2" },
{ 3,"ex3" },
};
public static Dictionary<int,string> B = new Dictionary<int,string>
{
{ 1,"ex1" },
{ 2,"ex2" },
{ 4,"ex4" }
};
public static void Main()
{
A.MergeInto(B);
foreach (var entry in B )
{
Console.WriteLine("{0}={1}", entry.Key, entry.Value);
}
}
}
Output:
1=ex1
2=EX2
3=ex3
Code on DotNetFiddle
Without preserving order
If all you want to do is keep the instance of B, but make it so all its elements match A, you can just do this:
B.Clear();
B.AddRange(A);
Preserving order
If you want to preserve order, you can still use the solution above, but you will need to sort the list that is passed to AddRange(). This is only a little more work.
First, create a lookup table which tells you the order that that Item values originally appeared in. A generic c# Dictionary uses a hash table for the keys, so this is going to end up being more efficient than scanning the List repeatedly. Note that we pass B.Count to the constructor so that it only needs to allocate space once rather than repeatedly as it grows.
var orderBy = new Dictionary<int,int>(B.Count);
for (int i=0; i<B.Count; i++) orderBy.Add(B[i].Item, i);
Now we use our solution, sorting the input list:
B.Clear();
B.AddRange
(
A.OrderBy( item => orderBy.GetValueOrFallback(item.Item, int.MaxValue) )
);
GetValueOrFallback is a simple extension method on Dictionary<,> that makes it simpler to deal with keys that may or may not exist. You pass in the key you want, plus a value to return if the key is not found. In our case we pass int.MaxValue so that new items will be appended to the end.
static public TValue GetValueOrFallback<TKey,TValue>(this Dictionary<TKey,TValue> This, TKey keyToFind, TValue fallbackValue)
{
TValue result;
return This.TryGetValue(keyToFind, out result) ? result : fallbackValue;
}
Example
Put it all together with a test program:
public class MyClass
{
public int Item { get; set; }
public string Value { get; set; }
public override string ToString() { return Item.ToString() + "," + Value; }
}
static public class ExtensionMethods
{
static public TValue ValueOrFallback<TKey,TValue>(this Dictionary<TKey,TValue> This, TKey keyToFind, TValue fallbackValue)
{
TValue result;
return This.TryGetValue(keyToFind, out result) ? result : fallbackValue;
}
static public void MergeInto(this List<MyClass> mergeFrom, List<MyClass> mergeInto)
{
var orderBy = new Dictionary<int,int>(mergeFrom.Count);
for (int i=0; i<mergeInto.Count; i++) orderBy.Add(mergeInto[i].Item, i);
mergeInto.Clear();
mergeInto.AddRange
(
mergeFrom.OrderBy( item => orderBy.ValueOrFallback(item.Item, int.MaxValue) )
);
}
}
public class Program
{
public static List<MyClass> A = new List<MyClass>
{
new MyClass { Item = 2,Value = "EX2" },
new MyClass { Item = 3,Value = "ex3" },
new MyClass { Item = 1,Value = "ex1" }
};
public static List<MyClass> B = new List<MyClass>
{
new MyClass { Item = 1,Value = "ex1" },
new MyClass { Item = 2,Value = "ex2" },
new MyClass { Item = 4,Value = "ex3" },
};
public static void Main()
{
A.MergeInto(B);
foreach (var b in B) Console.WriteLine(b);
}
}
Output:
1,ex1
2,EX2
3,ex3
Code on DotNetFiddle
This is what you specify in the question. I tested it and it works:
class myClass
{
public int item;
public string value;
//ctor:
public myClass(int item, string value) { this.item = item; this.value = value; }
}
static void updateList()
{
var listA = new List<myClass> { new myClass(1, "A1"), new myClass(2, "A2"), new myClass(3, "A3"), new myClass(4, "A4") };
var listB = new List<myClass> { new myClass(1, "B1"), new myClass(4, "B4"), new myClass(2, "B2"), new myClass(5, "B5") };
for (int i = 0; i < listB.Count; i++) //use index to be able to use RemoveAt which is faster
{
var b = listB[i];
var j = listA.FindIndex(x => x.item == b.item);
if (j >= 0) //A has this item, update its value
{
var v = listA[j].value;
if (b.value != v) b.value = v;
}
else //A does not have this item
{
listB.RemoveAt(i);
}
}
foreach (var a in listA)
{
//if (!listB.Contains(a)) listB.Add(a);
if (!listB.Any(b => b.item == a.item)) listB.Add(a);
}
}
You can do something similar to this:
var listA = new List<int> { 1, 3, 5 };
var listB = new List<int> { 1, 4, 3 };
//Removes items in B that aren't in A.
//This will remove 4, leaving the sequence of B as 1,3
listB.RemoveAll(x => !listA.Contains(x));
//Gets items in A that aren't in B
//This will return the number 5
var items = listA.Where(y => !listB.Any(x => x == y));
//Add the items in A that aren't in B to the end of the list
//This adds 5 to the end of the list
foreach(var item in items)
{
listB.Add(item);
}
//List B should be 1,3,5
Console.WriteLine(listB);
I have a function as follow. I need to return two parameters. First the index that is as list and it is done by the function. The other parameter that I need to return is a string str.
What would be your best suggestion for these outputs? a list with two different parameters? or what? Please let me know your ideas! Thanks
public List<int> index_selexted(TreeNodeCollection treeView, List<int> list)
{
List<int, List<string>> output_typ = new List<int, >();
foreach (TreeNode node in treeView)
{
if (node.Checked)
{
list.Add(node.Index);
string str = Regex.Match(node.Text, #" \((.*?)\) ").Groups[1].Value;
}
else
{
index_selexted(node.Nodes, list);
}
}
return list;
}
Well, since TreeNode.Index is not unique within the entire TreeNodeCollection then Dictionary<int, String> is not a choice, but Dictionary<int, List<String>> will do
//TODO: find a better name for dict
public Dictionary<int, List<String>> index_selexted(
TreeNodeCollection treeView,
Dictionary<int, List<String>> dict == null) { // == null for autocreation
if (null == treeView)
throw new ArgumentNullException("treeView");
if (null == dict)
dict = new Dictionary<int, List<String>>();
foreach (TreeNode node in treeView)
if (node.Checked) {
String match = Regex.Match(node.Text, #" \((.*?)\) ").Groups[1].Value;
List<String> list;
if (dict.TryGetValue(node.Index, out list))
list.Add(match);
else
dict.Add(node.Index, new List<String>() {match});
}
else
index_selexted(node.Nodes, dict);
return dict;
}
And so you'll have something like this as an output: index + all matches for it:
{1, ["abc", "def", "gh"]}
{3, ["xyz"]}
I've added dict == null in order to make the call easier:
// You don't have to pre-create the dictionary
var myDict = index_selexted(myTreeView.Nodes);
Use a Tuple
var res = new Tuple<string, List<string>>("string1", new List<string>());
You could either do
public class IndexSelectionResult{
public List<something> Index{get;set;}
public String StringResult
}
and return an instance of that, OR, if you're lazy, you can return a TUPLE:
public Tuple<List<string>, string>> myFunction(){ /* code */}
I believe you want this:
public static List<int> index_selexted(TreeNodeCollection treeView, out string str)
{
str = null;
var list = new List<int>();
var output_typ = new List<int>();
foreach (TreeNode node in treeView)
{
if (node.Checked)
{
list.Add(node.Index);
str = Regex.Match(node.Text, #" \((.*?)\) ").Groups[1].Value;
}
else
{
index_selexted(node.Nodes, list);
}
}
return list;
}
usage as such:
var treeview = sometreeview;
string output;
var result = index_selected(treeview, out output);
Console.WriteLine(output);
instead of using the list in your example (List> output_typ = new List();) consider
using a dictionary:
var output_typ = new Dictionary<int, List<string>>();
or a list of tuples:
var output_typ = new List<Tuple<int, List<string>>();
Hope this helps
There are a lot of ways to do this in fact, it depends on your specific use case which one you'd prefer.
Using a class
class MyResult {
public List<int> MyList { get; set; }
public string MyString { get; set; }
}
public MyResult index_selected(arg1..., arg2...) {
return new MyResult {
MyList = outputList,
MyString = "outputString"
}
}
Using a class is my preferred way. Though it may clutter if you have many return types it is by far the most readable solution.
Using a tuple
public Tuple<List<int>, string> index_selected(arg1..., arg2...) {
return Tuple.Create(outputList, "outputString");
}
My second goto option is a tuple. It is a lot more difficult to determine what the values contained in the tuple represent. But doesn't require creating more classes and is a quick solution (I use it mostly for private methods where readability is not much of a concern).
Using the out keyword
public List<int> index_selected(arg1..., out string resultString) {
resultString = null;
/* Doing calculations and such */
resultString = "
return outputList;
}
In this case the string passed to the resultString parameter will be replaced by whatever you assign to it in the method (see out keyword). Depending on your use case you may also want to look at the ref keyword.
This approach is rather error prone and is generally not preffered.
I need a helper function to convert string like "1=alice;2=bob;3=charlie" into a Dictionary<int, string>, and string like "1=true;2=false;3=true" into a Dictionary<int, bool>, and etc.
To do this, I wrote a lot of helper functions that are basically copy and pasted of each other:
private static void load(Dictionary<int, string> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = kv[1];
}
}
}
private static void load(Dictionary<int, bool> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = bool.Parse(kv[1]);
}
}
}
private static void load(Dictionary<int, int> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = int.Parse(kv[1]);
}
}
}
There are more, on other data types such as long, DateTime, and etc.
Is there a way to have just one helper function? I tried Dictionary<object, object> and it didn't work.
Yes, you should have a generic method instead. Personally I'd make it return a new dictionary rather than clearing an existing one, mind you... and use LINQ to implement it:
private static Dictionary<int, T> Load<T>(string text, Func<string, T> parser)
{
return s.Split(';')
.Where(item => item.Contains("="))
.Select(item => item.Split('=', 2))
.ToDictionary(pair => int.Parse(pair[0]), pair => parser(pair[1]));
}
Then call it with:
Dictionary<int, int> foo = Load(text, int.Parse);
Dictionary<int, bool> bar = Load(text, bool.Parse);
Dictionary<int, string> baz = Load(text, x => x);
You can make it generic and use Convert.ChangeType, which will try to parse any string input to the output type:
private static void load<T>(Dictionary<int, T> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = (T)Convert.ChangeType(kv[1], typeof(T));
}
}
}
Is there any way to not check every time if the key is already there?
Assuming that
we don't know all the possible keys before we iterate through list
the new way should work faster
var list = new List<Person>(); // { int Id; int DepartmentId; string Name; }
... // let's imagine that list is getting filled
var dict = new Dictionary<int, List<Person>>();
foreach (var p in list)
{
if(!dict.ContainsKey(p.DepartmentId))
dict.Add(p.DepartmentId, new List<int>());
dict[p.DepartmentId].Add(p);
}
I can see two improvements, however each of them inherently still checks for key presence.
First one reduces the number of accesses to the dictionary. It is theoretically possible that this will be faster, but computational complexity remains the same, and in practice the difference will probably be negligible:
1)
var dict = new Dictionary<int, List<Person>>();
foreach (var p in list)
{
List<Person> value = null;
if(!dict.TryGetValue(p.DepartmentId, out value))
{
value = new List<int>();
dict.Add(p.DepartmentId, value);
}
value.Add(p);
}
Second improvement adds some syntactic sugar:
2)
public static class DictionaryExtensions
{
public static TValue GetOrAdd<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key) where TValue : new()
{
TValue value;
if (!dictionary.TryGetValue(key, out value))
{
value = new TValue();
dictionary.Add(key, value);
}
return value;
}
}
And then:
foreach (var p in list)
{
dict
.GetOrAdd(p.DepartmentId)
.Add(p.DepartmentId);
}
As Servy pointed out, you may want to add a parameter for GetOrAdd extension to have more control over creation of the default value.