Im trying to get an enumberable collection from a dictionary held array.
Or should I say, I'm trying to write an extension method for my dictionary objects which store arrays to return an IEnumerable item when the result is null.
Im using dictionarys to store array datasets (there are speed reasons for this), which I extract at certain search points. the extracted data is used in Linq queries, joins etc but I have problems when a data set doesnt exist.
Returning an empty (0 count) row set would fix my problem. what I have so far is this (simplified code ofcourse)
public class Supplier
{
public string ID {get;set}
public string Name {get;set}
}
private sups[] = new Supplier[10];
Dictionary<int,Supplier[]> dic = new Dictionary<int, Supplier[]>();
dic.Add(1,sups[]);
public static IEnumerable<Supplier> TryGetValue<Tkey>(this IDictionary<Tkey, Supplier[]> source, Tkey ItemKey)
{
Supplier[] foundList;
IEnumerable<Supplier> retVal;
if (source.TryGetValue(ItemKey, out foundList))
{
retVal = foundList.AsEnumerable();
}
else
{
retVal = new Supplier[0].AsEnumerable();
}
return retVal;
}
// later in the code there is some thing like:
dic.TryGetValue(1).Count()
//or a linq join
from a in anothertable
join d in dic.TryGetValue(1) on a.ID equals d.ID
What im trying to acheive is a generic extension method like below:
public static IEnumerable<T> TryGetValue<Tkey,TValue>(this IDictionary<Tkey, TValue> source, Tkey ItemKey)
{
// same code...
// returning retVal = new T[0].AsEnumerable();
}
I keep getting close but never exactly there.... I would like to keep the extension method parameters simple. Its the passing of T which keeps catching me out.
If anybody can help then please send me your feed back.
many thanks in advance!
EDIT: Complications with type-inference.
Here's a way, the idea is to constrain the dictionary-values' type to be anIEnumerableof something.
Unfortunately, type-inference doesn't seem to work with this signature (tested with C# 3), so you will have to specify the generic arguments explicitly.
public static IEnumerable<TUnderlyingValue> GetValueOrEmpty<TKey, TUnderlyingValue, TValue>
(this IDictionary<TKey, TValue> source, TKey key)
where TValue : IEnumerable<TUnderlyingValue>
{
if(source == null)
throw new ArgumentNullException("source");
TValue retVal;
return source.TryGetValue(key, out retVal) ? retVal : Enumerable.Empty<TUnderlyingValue>;
}
Usage:
var dict = new Dictionary<string, int[]>
{
{ "foo", new[] { 6, 7, 8 } }
{ "bar", new[] { 1 } }
};
var fooOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("foo"); // { 6, 7, 8 }
var barOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("bar"); // { 1 }
var bazOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("baz"); // { }
Alternatively, we could use just 2 generic parameters without any constraints, but this will make the dictionary type less flexible. In this case, the compiler will infer the generic arguments just fine.
public static TUnderlyingValue[] GetValueOrEmpty<TKey, TUnderlyingValue>
(this IDictionary<TKey, TUnderlyingValue[]> source, TKey key)
{
if(source == null)
throw new ArgumentNullException("source");
TUnderlyingValue[] retVal;
return source.TryGetValue(key, out retVal) ? retVal : new TUnderlyingValue[0];
}
Related
Imagine this scenario: I need to manipulate (add, search and delete) items from a list of objects of type Book.
class Book{
int Id {get; set;}
string Title {get; set;}
string Author {get; set;}
int Year {get; set;}
// more properties
}
Constriants:
Id should be unique within the collection of Books
Title should be unique within the collection of Books
What I have so far, a Dictionary<int, Book> that has Id as a key and Book as a value. But in this case, If I want to add a new book to the dictionary I have to loop through all the values to check whether the Title is duplicate or not.
I start thinking about creating a HashSet only for Titles or having a second dictionary Dictionary<string, Book> that has Title as a key.
Any suggestion How to handle this scenario?
Edit:
As #David mentioned, I forgot to tell that my main concern here is performance. I want to lookup objects by Id and Title in the fastest way (O(1)).
You might use Tuple as the key:
var collection = new Dictionary<Tuple<int, string>, Book> (...);
var key = new Tuple<int, string>(1, "David"); // <<-----------
if(!collection.ContainsKey(key))
collection [key] = new Book(...);
Note that Tuple has its built in Equals() to make your life easier.
Update:
#AustinWBryan mentioned using ValueTuples (C# 7.0 feature) to replace Tuple, highly recommended. For more info about ValueTuples, refer to this link.
To ensure that both sides of the composite key are also unique a tuple won't cut it. Instead make your own key that checks for this in the equality checker.
public struct CompositeKey<T1, T2> : IEquatable<CompositeKey<T1, T2>>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public T1 Key1;
public T2 Key2;
public CompositeKey(T1 key1, T2 key2)
{
Key1 = key1;
Key2 = key2;
}
public override bool Equals(object obj) => obj is CompositeKey<T1, T2> && Equals((CompositeKey<T1, T2>)obj);
public bool Equals(CompositeKey<T1, T2> other)
{
return t1Comparer.Equals(Key1, other.Key1)
&& t2Comparer.Equals(Key2, other.Key2);
}
public override int GetHashCode() => Key1.GetHashCode();
}
So the dictionary works on buckets. It puts all the keys into buckets based on the hash code generated by GetHashCode(). Then it searches that bucket using a for loop over Equals(). The idea is that buckets should be as small as possible (ideally one item).
So we can control when a key will match, and how many buckets/items there are by controlling the hash code. If we return a constant hash code like 0, then everything is in the same bucket and it's down to the equality method to compare every item.
This comparer only returns the hash of the first key item. Assuming the first key item should be unique this is enough. Each bucket should still be one item, and when doing a lookup (that uses the full equals method) that's when the second key is also checked to ensure the type is the same value.
If you want to use ValueTuple as the key type you can pass in a custom comparer to the dictionary to achieve the same effect.
public class CompositeValueTupleComparer<T1, T2> : IEqualityComparer<(T1, T2)>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public bool Equals((T1, T2) x, (T1, T2) y) =>
t1Comparer.Equals(x.Item1, y.Item1) && t2Comparer.Equals(x.Item2, y.Item2);
public int GetHashCode((T1, T2) obj) => obj.Item1.GetHashCode();
}
new Dictionary<(int, string), Book>(new CompositeValueTupleComparer<int, string>());
It seems like both the ID and Name are going to be unique, as in, you shouldn't be able to use the same ID twice, regardless if the name has been used already. Otherwise, we'd end up with dict[3] referring to two different values.
Tuples or structs can't give that behavior, and still require you to loop. What you should instead do, is use a class similar to the one I've created:
public class TwoKeyDictionary<TKey1, TKey2, TValue>
{
public readonly List<TKey1> firstKeys = new List<TKey1>();
public readonly List<TKey2> secondKeys = new List<TKey2>();
public readonly List<TValue> values = new List<TValue>();
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
if (firstKeys.Contains(key1)) throw new ArgumentException();
if (secondKeys.Contains(key2)) throw new ArgumentException();
firstKeys.Add(key1);
secondKeys.Add(key2);
values.Add(value);
}
public void Remove(TKey1 key) => RemoveAll(firstKeys.IndexOf(key));
public void Remove(TKey2 key) => RemoveAll(secondKeys.IndexOf(key));
private void RemoveAll(int index)
{
if (index < 1) return;
firstKeys.RemoveAt(index);
secondKeys.RemoveAt(index);
values.RemoveAt(index);
}
public TValue this[TKey1 key1]
{
get
{
int index = firstKeys.IndexOf(key1);
if (index < 0) throw new IndexOutOfRangeException();
return values[firstKeys.IndexOf(key1)];
}
}
public TValue this[TKey2 key2]
{
get
{
int index = secondKeys.IndexOf(key2);
if (index < 0) throw new IndexOutOfRangeException();
return values[secondKeys.IndexOf(key2)];
}
}
}
And then you can use it like this:
var twoDict = new TwoKeyDictionary<int, string, float>();
twoDict.Add(0, "a", 0.5f);
twoDict.Add(2, "b", 0.25f);
Console.WriteLine(twoDict[0]); // Prints "0.5"
Console.WriteLine(twoDict[2]); // Prints "0.25"
Console.WriteLine(twoDict["a"]); // Prints "0.5"
Console.WriteLine(twoDict["b"]); // Prints "0.25"
twoDict.Add(0, "d", 2); // Throws exception: 0 has already been added, even though "d" hasn't
twoDict.Add(1, "a", 5); // Throws exception: "a" has already been added, even though "1" hasn't
The TwoKeyDictionary would need to implement ICollection, IEnumerable, etc., to do the full behavior stuff
Recently, I have found out that indexer can accept an array of arguments as params:
public class SuperDictionary<TKey, TValue>
{
public Dictionary<TKey, TValue> Dict { get; } = new Dictionary<TKey, TValue>();
public IEnumerable<TValue> this[params TKey[] keys]
{
get { return keys.Select(key => Dict[key]); }
}
}
Then, you will be able to do:
var sd = new SuperDictionary<string, object>();
/* Add values */
var res = sd["a", "b"];
However, I never met such usage in .NET Framework or any third-party libraries. Why has it been implemented? What is the practical usage of being able to introduce params indexer?
The answer has been found in a minute after posting the question and looking through the code and documentation - C# allows you to use any type as a parameter for indexer, but not params as a special case.
According to MSDN,
Indexers do not have to be indexed by an integer value; it is up to you how to define the specific look-up mechanism.
In other words, indexer can be of any type. It can either be an array...
public IEnumerable<TValue> this[TKey[] keys]
{
get { return keys.Select(key => Dict[key]); }
}
var res = sd[new [] {"a", "b"}];
or any kind of another unusual type or collection, including params array, if it seems to be convenient and suitable in your case.
Imagine I have class like this:
class MyKey : IComparable<MyKey> {
public int Key { get; private set; }
public MyKey(int key) { Key = key; }
public int CompareTo(MyKey that) {
return that.Key - this.Key;
}
}
Furthermore, I have a generic wrapper class like this:
class MyListWrapper<T> where T : MyKey
{
private List<T> list;
public MyListWrapper(IEnumerable<T> items)
{
list = new List<T>(items);
list.Sort();
}
public int Search(T searchKey)
{
return list.BinarySearch(searchKey);
}
}
This allows people to store custom class inheriting from MyKey, and it works perfectly fine. However, it would also make sense to be able to search using a MyKey as we know T is a MyKey, and the list is sorted using MyKey's Key:
public int Search(MyKey searchKey)
{
return list.BinarySearch(searchKey); // Does not compile!
}
However, this doesn't compile, since BinarySearch takes a T (T could be any custom class).
Neither does it work, if I provide the comparer. Imagine MyKey wasn't comparable, but I made a custom comparer that used Key instead. I could use it when sorting and when searching.
Is is possible to search the list using MyKey somehow? I don't like storing the list as List<MyKey> and cast the values when I use them (that defeats the purpose of the generic list). I can't cast the list of type List<T> to List<MyKey> either.
You can create a wrapper class which inherits from MyNamedKey or create new instance of MyNamedKey itself just to search the item.
var mySearchKey = new MyKey { Key = 2 };
var index = list.BinarySearch(new MyNamedKeyWrapper(mySearchKey));
class MyNamedKeyWrapper : MyNamedKey
{
public MyNamedKeyWrapper(MyKey key)
{
this.Key = key.Key;
}
}
This will help you to maintain O(log n) while adding small allocation cost.
Or if you prefer to use brittle reflection, you can.. Get the instance of underlying array and cast it to MyKey[] (This works because arrays are covariant) and use Array.BinarySearch.
var array = (MyKey[])list.GetType()
.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(list);
var index = Array.BinarySearch(array, mySearchKey);
Edit: Since you don't know the most derived type, if you constraint new() with your generic parameter, you can achieve what you're looking for
class MyListWrapper<T> where T : MyKey, new()
{
private readonly List<T> list;
public MyListWrapper(IEnumerable<T> items)
{
list = new List<T>(items);
list.Sort();
}
public int Search(MyKey searchKey)
{
T dummyKey = new T() { Key = searchKey.Key };
return list.BinarySearch(dummyKey);
}
}
Linq is what you're after!
First, make sure you have System.Linq referenced in your usings.
Then you can use the following code to get all matching list items:
IEnumerable<MyNamedKey> found = list.Where(l => l.Key == 2);
To get a single item, use:
MyNamedKey found = list.FirstOrDefault(l => l.Key == 2);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Bidirectional 1 to 1 Dictionary in C#
Im curious if a datastructure exists in the standard .net libraries that can represent a 1-1 relationship, such as the following
1-a
4-b
6-c
5-d
Where I can say:
thisstructure[1] // returns "a"
thisstructure.GetKey["d"] // return 5
I understand all keys would have to be unique, does anything similar exist?
Thanks!
Yes- it's called KeyedCollection. It is intended to be subclassed and provides indexed access as well as access by a property derived from the added item. I usually make a generic subclass:
public class GenericKeyedCollection<TKey, TValue> : KeyedCollection<TKey, TValue> {
private readonly Func<TValue, TKey> _keyGenerator;
public GenericKeyedCollection(Func<TValue, TKey> keyGenerator) {
_keyGenerator = keyGenerator;
}
protected override int GetKeyForItem(TValue item)
{
return _keyGenerator(item);
}
}
To use it:
var myCollection = new GenericKeyedCollection<String, Car>(c=>c.Model);
myCollection.Add(new Car("Ford", "Mustang"));
var byIndex = myCollection[0];
var byModel = myCollection["Mustang"];
The only caveat is that the derived property (the "key") mustn't change after the item has been added.
If your key is not a property of the value, then you can use a Tuple<T1, T2> to combine the key and value:
var myCollection = new GenericKeyedCollection<String, Tuple<String, Car>>(t=>t.Item1);
myCollection.Add(new Tuple<String, Car>("Foo", Car("Ford", "Mustang")));
var byIndexCar = myCollection[0].Item2;
var byItem1Car = myCollection["Foo"].Item2;
Could this method fit your needs?
public static class Extensions
{
public static TKey GetKey<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue value)
{
int index = dict.Values.ToList().IndexOf(value);
if (index == -1)
{
return default(TKey); //or maybe throw an exception
}
return dict.Keys.ToList()[index];
}
}
You could then use it like so:
Dictionary<int, char> dict = new Dictionary<int, char>();
dict.Add(1, 'a');
dict.Add(4, 'b');
dict.Add(6, 'c');
dict.Add(5, 'd');
Console.WriteLine(dict.GetKey('d')); //5
The Dictionary....or IDictionary interface is the closest I can think of to what you want. It doesn't have quite so simple a searching operation, in that searching on a value can return the key, but I do know you can search on a key to get a value. providing functionality for the reverse in a custom extended class wouldn't be difficult at all.
MSDN IDictionary page
i am new to .net 3.5.
I have a collection of items:
IList<Model> models;
where
class Model
{
public string Name
{
get;
private set;
}
}
I would like to get the element, which has the longest name's length.
I tried
string maxItem = models.Max<Model>(model => model.Name.Length);
but it of course returns the maximum length (and I need a Model object). I know there is a way of doing this using the extension methods but I don't know how.
There isn't a built-in way of doing this, unfortunately - but it's really easy to write an extension method to do it.
It was in one of my very first blog posts, in fact... note that there's a better implementation in one of the comments. I'll move it into the body if I get time.
EDIT: Okay, I have a slightly abbreviated version - it just returns the maximal element, using the given selector. No need to do a projection as well - do that once afterwards if you need to. Note that you could remove the constraint on TValue and use Comparer<TValue>.Default instead, or have an overload which allows the comparison to be specified as another parameter.
public static TSource MaxBy<TSource, TValue>(this IEnumerable<TSource> source,
Func<TSource, TValue> selector)
where TValue : IComparable<TValue>
{
TValue maxValue = default(TValue);
TSource maxElement = default(TSource);
bool gotAny = false;
foreach (TSource sourceValue in source)
{
TValue value = selector(sourceValue);
if (!gotAny || value.CompareTo(maxValue) > 0)
{
maxValue = value;
maxElement = sourceValue;
gotAny = true;
}
}
if (!gotAny)
{
throw new InvalidOperationException("source is empty");
}
return maxElement;
}
Sample use: (note type inference):
string maxName = models.MaxBy(model => model.Name.Length).Name;
Here's another way of doing it. There's a version of Max that takes no criterion, and uses IComparable. So we could provide a way to wrap anything in a comparable object, with a delegate providing the comparison.
public class Comparable<T> : IComparable<Comparable<T>>
{
private readonly T _value;
private readonly Func<T, T, int> _compare;
public Comparable(T v, Func<T, T, int> compare)
{
_value = v;
_compare = compare;
}
public T Value { get { return _value; } }
public int CompareTo(Comparable<T> other)
{
return _compare(_value, other._value);
}
}
Then we can say:
Model maxModel = models.Select(m => new Comparable<Model>(m, (a, b) => a.Name.Length - b.Name.Length)).Max().Value;
This involves a lot of extra allocation, but it's sort of academically interesting (I think).
This is how I got it to work. Maybe there's a better way, I'm not sure:
decimal de = d.Max(p => p.Name.Length);
Model a = d.First(p => p.Name.Length == de);
You can use Aggregate. It can be done without writing new extension method.
models.Aggregate(
new KeyValuePair<Model, int>(),
(a, b) => (a.Value < b.Name.Length) ? new KeyValuePair<Model, int>(b, b.Name.Length) : a,
a => a.Key);
Is there anything gained by using the extension methods?
Perhaps a method or procedure with a simple iteration of the list would suffice?
Something to the effect of
Dim result as string = models(0).Name
for each m as Model in models
if m.Name.length > result.length then
result = m.Name
end if
next
Another way might be:
var item = (from m in models select m orderby m.Name.Length descending).FirstOrDefault();
First one will be the one with the longest length.