I'm newb with generics/iterators/enumerators etc.
I have code, it keeps field number (int) and error mesages (List string) for each field:
public class ErrorList : IEnumerable // ?
{
private Dictionary <int, List<string>> errorList;
// ...
}
How to make this class work with foreach loop? I wanna use GetEnumerator form Dictionary, but how should i do this?
You could simply provide a public GetEnumerator method:
public class ErrorList
{
private Dictionary<int, List<string>> errorList = new Dictionary<int, List<string>>();
... some methods that fill the errorList field
public IEnumerator<KeyValuePair<int, List<string>>> GetEnumerator()
{
return errorList.GetEnumerator();
}
}
and now assuming you have an instance of ErrorList:
var errors = new ErrorList();
you can loop through them:
foreach (KeyValuePair<int, List<string>> item in errors)
{
...
}
Dictionary implements IEnumerable<KeyValuePair<TKey, TValue>>, so this works:
foreach (KeyValuePair<Int, List<String>> kvp in errorList) {
var idx = kvp.Key;
var vals = kvp.Value;
// ... do whatever here
}
You can simply return errorList.GetEnumerator().
Related
I have the following list (ignore the LST ==> part):
LST ==>Username
LST ==>Password
LST ==>SampleRequestValue
LST ==>SampleComplexRequest
LST ==>SampleComplexRequest.SampleTestBValue
LST ==>SampleComplexRequest.SampleTestAValue
and the following Key List from a dictionary (ignore the DICT ==> part):
DICT ==>Password
DICT ==>Username
DICT ==>SampleRequestValue
DICT ==>SampleComplexRequest.SampleTestAValue
DICT ==>SampleComplexRequest.SampleTestBValue
I want the dictionary sorted in the order of the list (i.e. Username before Password).
Saw a few samples on SO of sorta / kinda / not really examples for this... but not really a similar scenario. Also want it to be as fast as possible rather then brute forcing it.
LST may have more items then DICT. I only care about sorting DICT. DICT will always have a matching entry in LST. I just want the DICT by LST order.
Another way to do this would be to write a small custom comparer class that uses a list to determine comparison values:
public class ListComparer : IComparer<string>
{
public List<string> ComparisonList { get; set; }
public ListComparer(List<string> comparisonList)
{
ComparisonList = comparisonList;
}
public int Compare(string x, string y)
{
if (ComparisonList == null || !ComparisonList.Contains(x))
return 1;
if (ComparisonList.Contains(y))
return ComparisonList.IndexOf(x).CompareTo(ComparisonList.IndexOf(y));
return -1;
}
}
Then you can pass this to the constructor of a SortedDictionary, which will then use it each time an item is added to the dictionary. This way you don't have to call OrderBy on the dictionary every time new values are added (which also has the negative side-effect of creating a whole new dictionary each time).
Here's a code sample that may help. Notice that we add "Password" first, then "Username", but when we output the items, they are in the expected order:
static void Main()
{
var comparisonList = new List<string>
{
"Username",
"Password",
"SampleRequestValue",
"SampleComplexRequest",
"SampleComplexRequest.SampleTestBValue",
"SampleComplexRequest.SampleTestAValue",
};
// Add items in an "unorderd" order
var items = new SortedDictionary<string, string>(new ListComparer(comparisonList))
{
{"Password", "LetMeIn"},
{"Username", "JohnDoe"}
};
foreach (var item in items)
{
Console.WriteLine($"{item.Key} = {item.Value}");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
I just saw that you don't have control over the initial dictionary, but are willing to create a new one. In that case, you can simply use the overload constructor that takes in a dictionary and a comparer, and it will be automatically sorted on your list:
var sortedItems = new SortedDictionary<string, string>(
originalDictionary, new ListComparer(comparisonList));
Using LINQ this is pretty easy.
First, setup a Dictionary<string,int> to represent your desired sort order:
var orderList = new[] { "Username", "Password", "SampleRequestValue", "SampleComplexRequest", "SampleComplexRequest.SampleTestBValue", "SampleComplexRequest.SampleTestAValue" }
.ToList();
var sortOrder = orderList
.Select((s, p) => new { s, p })
.ToDictionary(sp => sp.s, sp => sp.p);
Then you can sort the DictionaryEntrys from the OrderedDictionary:
var ans = src.Cast<DictionaryEntry>().OrderBy(de => sortOrder[(string)de.Key]);
If you want the answer an an OrderedDictionary, you can convert back using an extension method:
public static class IEnumerableExt {
public static OrderedDictionary ToOrderedDictionary<TKey,TValue,TObj>(this IEnumerable<TObj> src, Func<TObj,TKey> keyFn, Func<TObj, TValue> valueFn) {
var ans = new OrderedDictionary();
foreach (var s in src)
ans.Add(keyFn(s), valueFn(s));
return ans;
}
}
Now just use the extension method:
var odans = ans.ToOrderedDictionary(s => s.Key, s => s.Value);
Just use the OrderBy method :
var SortedDic = dic.OrderBy(o => lst.IndexOf(o.Key));
Here are three extension methods that will do what you want in different ways.
They are used like this (KeySortItems and ValueSortItems are different List<string> lists:
DictionaryItems.ListOrderByKey(KeySortItems)
DictionaryItems.ListOrderByValue(ValueSortItems)
DictionaryItems.ListOrderBy(KeySortItems, (kv, value) => kv.Key.Equals(value))
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderByKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (kv.Key.Equals(value))
{
yield return kv;
break;
}
}
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (kv.Value.Equals(value))
yield return kv;
}
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderBy<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list, Func<KeyValuePair<TKey, TValue>, TValue, bool> func)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (func.Invoke(kv, value))
yield return kv;
}
Copy and paste console app.
using System;
using System.Collections.Generic;
public class Program
{
public static List<string> KeySortItems { get; set; } = new List<string>()
{
"Username",
"Password",
"Item not in Dictionary",
"SampleRequestValue",
"SampleComplexRequest"
};
public static List<string> ValueSortItems { get; set; } = new List<string>()
{
"Mathew",
"1234",
"Item not in Dictionary",
"Sample Request",
"Something Complex"
};
public static Dictionary<string, string> DictionaryItems { get; set; } = new Dictionary<string, string>()
{
["Password"] = "1234",
["Username"] = "Mathew",
["SampleComplexRequest"] = "Something Complex",
["SampleRequestValue"] = "Sample Request"
};
public static void Main()
{
Console.WriteLine("Original Dictionary");
foreach (var kv in DictionaryItems)
{
Console.WriteLine($"{kv.Key} : { kv.Value}");
}
Console.WriteLine("\nSorted by Key");
foreach (var kv in DictionaryItems.ListOrderByKey(KeySortItems))
{
Console.WriteLine($"{kv.Key} : { kv.Value}");
}
Console.WriteLine("\nSorted by Value");
foreach (var kv in DictionaryItems.ListOrderByValue(ValueSortItems))
{
Console.WriteLine($"{kv.Key} : { kv.Value}");
}
Console.WriteLine("\nSorted by Keys via func");
foreach (var kv in DictionaryItems.ListOrderBy(KeySortItems, (kv, value) => kv.Key.Equals(value)))
{
Console.WriteLine($"{kv.Key} : { kv.Value}");
}
Console.ReadKey();
}
}
public static class Extensions
{
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderByKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (kv.Key.Equals(value))
yield return kv;
}
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (kv.Value.Equals(value))
yield return kv;
}
public static IEnumerable<KeyValuePair<TKey, TValue>> ListOrderBy<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, List<TValue> list, Func<KeyValuePair<TKey, TValue>, TValue, bool> func)
{
foreach (var value in list)
foreach (var kv in dictionary)
if (func.Invoke(kv, value))
yield return kv;
}
}
//OUTPUT
//Original Dictionary
//Password : 1234
//Username : Mathew
//SampleComplexRequest : Something Complex
//SampleRequestValue : Sample Request
//Sorted by Key
//Username : Mathew
//Password : 1234
//SampleRequestValue : Sample Request
//SampleComplexRequest : Something Complex
//Sorted by Value
//Username : Mathew
//Password : 1234
//SampleRequestValue : Sample Request
//SampleComplexRequest : Something Complex
//Sorted by Keys via func
//Username : Mathew
//Password : 1234
//SampleRequestValue : Sample Request
//SampleComplexRequest : Something Complex
I have a list of dictionary:
List<Dictionary<string, string>> items = new List<Dictionary<string, string>>();
foreach (var group in groupedItems)
{
foreach (var item in group)
{
Dictionary<string, string> newItem = new Dictionary<string, string>();
newItem.Add("name", item.Name);
newItem.Add("value", item.Value);
}
}
items.Add(newItem);
Basically when I loop through the grouped items, I create a Dictionary where the key is the item.Name and value is item.Value. In a grouped case, this will result in duplicate dictionaries to the list.
How can I avoid adding duplicate Dictionary to this List?
I have a foreach loop and I want to add some items once.
First thing that comes to mind would be to create your own class which extends Dictionary<string, string> and implement your own version of GetHashCode() and Equals:
public class MyDictionary : Dictionary<string, string>
{
public override int GetHashCode()
{
...
}
public override bool Equals(object source)
{
...
}
}
Within the Equals you implement your equality mechanism, and in GetHashCode you implement a mechanism which yields the same numeric value for two dictionaries which are the same, according to your equality criteria.
Then, instead of a List<Dictionary<string, string>>, you use a HashSet<MyDictionary>. Since sets do not allow duplicates, you should end up with a collection of unique dictionary collections.
I solved this in this way:
I created a new dictionary:
Dictionary<string, string> control = new Dictionary<string, string>();
And then I just do like this:
Dictionary<string, string> newItem = new Dictionary<string, string>();
newItem.Add("name", item.Name);
newItem.Add("value", item.Value);
if (!control.ContainsKey(item.Name))
{
control.Add(item.Name);
items.Add(newItem);
}
You can implement your own EqualityComparer to determine if two dictionaries are equal:
class EqualityComparer<Dictionary<string, string>> : IEqualityComparer<Dictionary<string, string>>
{
public bool Equals(Dictionary<string, string> x, Dictionary<string, string> y)
{
// your code here
}
public int GetHashCode(Dictionary<string, string> obj)
{
// your code here
}
}
Now you may use this comparer within a check for existance of a new item:
foreach (var g in groupedItems)
{
Dictionary<string, string> newItem = new Dictionary<string, string>();
foreach(var item in g)
{
newItem.Add("name", item.Name);
newItem.Add("value", item.Value);
}
if (!items.Contains(newItem, new EqualityComparer()) items.Add(newItem);
}
Thus there is no need to create a new implementation of Dictionary.
Trying to copy values from an existing NameValueCollection object to a Dictionary. I have the following code below to do that but seems the Add does not accept that my keys and values are as Strings
IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
public void copyFromNameValueCollection (NameValueCollection a)
{
foreach (var k in a.AllKeys)
{
dict.Add(k, a[k]);
}
}
Note: NameValueCollection contains String keys and values and so I simply want to provide here a method to allow copying of those to a generic dictionary.
Extension method plus linq:
public static Dictionary<string, string> ToDictionary(this NameValueCollection nvc) {
return nvc.AllKeys.ToDictionary(k => k, k => nvc[k]);
}
//example
var dictionary = nvc.ToDictionary();
It doesn't make sense to use generics here since you can't assign strings to some arbitrary generic type:
IDictionary<string, string> dict = new Dictionary<string, string>();
public void copyFrom(NameValueCollection a)
{
foreach (var k in a.AllKeys)
{
dict.Add(k, a[k]);
}
}
although you should probably create a method to create a new dictionary instead:
public static IDictionary<string, string> ToDictionary(this NameValueCollection col)
{
IDictionary<string, string> dict = new Dictionary<string, string>();
foreach (var k in col.AllKeys)
{
dict.Add(k, col[k]);
}
return dict;
}
which you can use like:
NameValueCollection nvc = //
var dictionary = nvc.ToDictionary();
If you want a general way of converting the strings in the collection into the required key/value types, you can use type converters:
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this NameValueCollection col)
{
var dict = new Dictionary<TKey, TValue>();
var keyConverter = TypeDescriptor.GetConverter(typeof(TKey));
var valueConverter = TypeDescriptor.GetConverter(typeof(TValue));
foreach(string name in col)
{
TKey key = (TKey)keyConverter.ConvertFromString(name);
TValue value = (TValue)valueConverter.ConvertFromString(col[name]);
dict.Add(key, value);
}
return dict;
}
parameters.AllKeys.ToDictionary(t => t, t => parameters[t]);
Use LINQ:
public static IDictionary<string, string> ToDictionary(this NameValueCollection collection)
{
return collection.Cast<string>().ToDictionary(k => k, v => collection[v]);
}
Usage:
IDictionary<string, string> dic = nv.ToDictionary();
Super-Short Version
var dataNvc = HttpUtility.ParseQueryString(data);
var dataCollection = dataNvc.AllKeys.ToDictionary(o => o, o => dataNvc[o]);
If you know that your dictionary is always going to contain strings, specify it to contain strings instead of making your class generic:
IDictionary<string, string> dict = new Dictionary<string, string>();
With this, things will "just work" as written (without the generic method specification).
If you need this to be a generic class, and hold generic data, you need some way to convert from string to TKey and string to TValue. You could provide delegates to your copy method to do this:
public void CopyFrom(NameValueCollection a, Func<string, TKey> keyConvert, Func<string, TValue> valueConvert)
{
foreach(var k in a.AllKeys)
{
dict.Add(keyConvert(k), valueConvert(a[k]));
}
}
You would then need to pass a delegate in that would perform the conversion from string to TValue and string to TKey.
You should not forget about EqualityComparer. But it is not a public property. So, you should use reflection to get it.
public static IEqualityComparer GetEqualityComparer(this NameObjectCollectionBase nameObjectCollection)
{
PropertyInfo propertyInfo = typeof(NameObjectCollectionBase).GetProperty("Comparer", BindingFlags.Instance | BindingFlags.NonPublic);
return (IEqualityComparer)propertyInfo.GetValue(nameObjectCollection);
}
public static IEqualityComparer<string> GetEqualityComparer(this NameValueCollection nameValueCollection)
{
return (IEqualityComparer<string>)((NameObjectCollectionBase)nameValueCollection).GetEqualityComparer();
}
public static Dictionary<string, string> ToDictionary(this NameValueCollection nameValueCollection)
{
Dictionary<string, string> dictionary =
nameValueCollection.AllKeys.ToDictionary(x => x, x => nameValueCollection[x], nameValueCollection.GetEqualityComparer());
return dictionary;
}
I have a list of strings, and in this list of strings there might be references to a list of other strings. For instance, suppose the list is like so: [a.txt, b.txt, c.more], and when I iterate over the list I'd like to lookup in a dictionary: {{'c.more', [c.txt, d.txt]}} so that the resulting list is [a.txt, b.txt, c.txt, d.txt] as a result of looking up c.more in the dictionary.
What I have at this point is something like this:
var dict = new Dictionary<string,List<string>>
{
{"c.more", new List<string> { "c.txt", "d.txt" } }
}
list.SelectMany(
f =>
f.EndsWith(".more")
? Expand(f)
: Include(f, dict))
Where Expand and Include do this:
public IEnumerable<string> Include(string f) { yield return f; }
public IEnumerable<string> Expand(string f, Dictionary<string,List<string>> dict) {
return dict.ContainsKey(f) ? dict[f] : new List<string>();
}
I could simply return a new List<string> { f } in the first half of the ternary and the result of doing the lookup in the second half, but I want to later handle a recursive lookup so I'm farming out the Expand. Right now I'm not really concerned about memory usage, but I felt like there might be some other way of doing what I'm after that I haven't seen yet.
Is there a better approach to expand a list with more lists?
You might not need an answer anymore, but I still want to attempt.
An option is to create your own class inheriting from IEnumerable. Take the following:
public class LookupList : IEnumerable<string>
{
private readonly IEnumerable<string> _source;
private Dictionary<string, List<string>> _referenceDic;
public LookupList(IEnumerable<string> source, Dictionary<string, List<string>> referenceDic)
{
_source = source;
_referenceDic = referenceDic;
}
public IEnumerator<string> GetEnumerator()
{
foreach (string item in _source)
{
//check if it's in the ref dictionary, if yes: return only sub items, if no: return the item
if (_referenceDic.Keys.Contains(item))
{
foreach (string dicItem in _referenceDic[item])
yield return dicItem;
}
else
{
yield return item;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Now run the following lines to access the items.
Dictionary<string, List<string>> refData = new Dictionary<string, List<string>>();
LookupList lst = new LookupList(new List<string>() { "a.txt", "b.txt", "c.more" }, refData);
refData.Add("c.more", new List<string>() { "c.txt", "d.txt" });
List<string> flattenedItems = lst.ToList();
I am trying to do the following but I think I must be missing something...(fairly new to generics)
(Need to target .NET 2.0 BTW)
interface IHasKey
{
string LookupKey { get; set; }
}
...
public static Dictionary<string, T> ConvertToDictionary(IList<T> myList) where T : IHasKey
{
Dictionary<string, T> dict = new Dictionary<string, T>();
foreach(T item in myList)
{
dict.Add(item.LookupKey, item);
}
return dict;
}
Unfortunately, this gives a "Constraints are not allowed on non-generic declarations" error. Any ideas?
You have not declared the generic parameter.
Change your declaration to:
public static Dictionary<string, T> ConvertToDictionary<T> (IList<T> myList) where T : IHasKey{
}
Try something like this
public class MyObject : IHasKey
{
public string LookupKey { get; set; }
}
public interface IHasKey
{
string LookupKey { get; set; }
}
public static Dictionary<string, T> ConvertToDictionary<T>(IList<T> myList) where T: IHasKey
{
Dictionary<string, T> dict = new Dictionary<string, T>();
foreach(T item in myList)
{
dict.Add(item.LookupKey, item);
}
return dict;
}
List<MyObject> list = new List<MyObject>();
MyObject o = new MyObject();
o.LookupKey = "TADA";
list.Add(o);
Dictionary<string, MyObject> dict = ConvertToDictionary(list);
You forgot the Generic Paramter in the method
public static Dictionary<string, T> ConvertToDictionary<T>(IList<T> myList) where T: IHasKey
Since the classes in the input list are different (as you say in your comment) you can either implement it like suggested by #orsogufo, or you could just as well implement your signature on the interface itself:
public static Dictionary<string, IHasKey> ConvertToDictionary(IList<IHasKey> myList)
{
var dict = new Dictionary<string, IHasKey>();
foreach (IHasKey item in myList)
{
dict.Add(item.LookUpKey, item);
}
return dict;
}
Using the generic declaration is best if you have a list of one specific implementation of the interface as noted in the comments to the other answer.