Pre order generic list C# - c#

I'm looking for a sorted list in C#, but when I inserting a item not sorting after insert all.
Neither SortedList nor Dictionary are suitable because I may have duplicate keys.
For example:
list.Insert(1001, v1);
list.Insert(1002, v2);
list.Insert(1002, v3);
list.Insert(1003, v4);

One possibility is to write a custom comparer which would allow duplicate keys in your collection:
public class DuplicateKeyComparer<TKey> : IComparer<TKey> where TKey : IComparable
{
public int Compare(TKey x, TKey y)
{
var res = x.CompareTo(y);
return res == 0 ? 1 : res;
}
}
and then use a SortedList<TKey, TValue>:
var comparer = new DuplicateKeyComparer<int>();
var list = new SortedList<int, string>(comparer);
list.Add(1001, "v1");
list.Add(1002, "v2");
list.Add(1002, "v3");
list.Add(1003, "v4");
Obviously there are culprits with this approach that you should be aware of - you will never be able to remove any key from this collection. So if you intend to use list.Remove(1002) as an example, then you will need an alternative approach.

Related

Creating a custom equality comparer for IEnumerables<T> when T is IEnumerable

I want to have a custom equality comparer IEnumerables. using #PaulZahra's code, I created the following class:
class CustomEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
var enumerables = new Dictionary<T, uint>();
foreach (T item in x)
{
enumerables.Add(item, 1);
}
foreach (T item in y)
{
if (enumerables.ContainsKey(item))
{
enumerables[item]--;
}
else
{
return false;
}
}
return enumerables.Values.All(v => v == 0);
}
public int GetHashCode(IEnumerable<T> obj) => obj.GetHashCode();
}
The problem is that if T itself is an IEnumerable, then ContainsKey will check for reference equality, while the point of this equality comparer is to check for value equality at any given depth.
I thought to use .Keys.Contains() instead, since it can accept an IEqualityComparer as an argument:
if (enumerables.Keys.Contains(item, this)) // not sure if "this" or a new object
but I get the following error (CS1929):
'Dictionary.KeyCollection' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains(IQueryable, T, IEqualityComparer)' requires a receiver of type 'IQueryable'
I am not sure how to approach this. How to fix it? Thanks.
Edit: Note that this comparer doesn't care about order.
As others have mentioned, IEnumerable<T> can enumerate forever, so it's dangerous to do this on that interface. I'd recommend using ICollection<T> instead- it has a fixed size. And you'll find it will work for most any type you'd want to use anyway.
Furthermore, I'd recommend using TryGetValue to reduce the number of times you need to look up into the dictionary.
Your code is not correctly keeping the count of each item in the first enumerable.
GetHashCode needs to take into account every member of the enumerable.
All that being said, here is an adjustment of your implementation:
class CustomEqualityComparer<T> : IEqualityComparer<ICollection<T>>
{
public bool Equals(ICollection<T> x, ICollection<T> y)
{
if (x.Count != y.Count) return false;
var enumerables = new Dictionary<T, uint>(x.Count);
foreach (T item in x)
{
enumerables.TryGetValue(item, out var value);
enumerables[item] = value + 1;
}
foreach (T item in y)
{
var success = enumerables.TryGetValue(item, out var value);
if (success)
{
enumerables[item] = value - 1;
}
else
{
return false;
}
}
return enumerables.Values.All(v => v == 0);
}
public int GetHashCode(ICollection<T> obj)
{
unchecked
{
var hashCode = 0;
foreach(var item in obj)
{
hashCode += (item != null ? item.GetHashCode() : 0);
}
return hashCode;
}
}
}
To have such recursive comparer you simply need pass proper comparer to Dictionary if T is enumerable. I think getting type T from IEnumerable<T> and then equivalent of new Dictionary<U, uint>(new CustomEqualityComparer<U>) (using Create instance of generic type?) should achieve what you want.
Notes:
you must provide correct implementation of GetHashCode that matches Equals if you use comparer for any dictionary/HashSet. Default Equals for sequences is reference compare that does not align with your Equals. Note that most implementation of GetHashCode depend on order of the items in the collection - so you need to find one that works for sets. I.e. sum of hash codes of each item would do, probably making distribution slightly worse.
you may want to LINQ set operations instead of doing them by hand. All operations like Distinct already take comparers. In case of "sets are the same" you can use Distinct - x.Distinct(y, comparerBuiltViaReflection)
Beware of limitations of such code: not every enumerable can be enumerated more than once (user input, network streams,..) or may produce different result on re-iteration (while(count < 10){ count ++; yield return random.Next(); }), cost of re-iteartion many be significant (re-read all lines in huge file on each iteration) or enumerable can represent infinite sequence (while(true){ yield return count++; }).

How to index the Values property of C# Dictionary

Using the Values property of C# Dictionary,
var myDict = Dictionary < string, object> ;
How would I get the values in
myDict.Values
I tried
var theVales = myDict.Values ;
object obj = theValues[0] ;
But that is a syntax error.
Added:
I am trying to compare the values in two dictionaries that have
the same keys
You can't. The values do not have a fixed order. You could write the values into a new List<object> and index them there, but obviously that's not terribly helpful if the dictionary's contents change frequently.
You can also use linq: myDict.Values.ElementAt(0) but:
The elements will change position as the dictionary grows
It's really inefficient, since it's just calling foreach on the Values collection for the given number of iterations.
You could also use SortedList<TKey, TValue>. That maintains the values in order according to the key, which may or may not be what you want, and it allows you to access the values by key or by index. It has very unfortunate performance characteristics in certain scenarios, however, so be careful about that!
Here's a linq solution to determine if the values for matching keys also match. This only works if you're using the default equality comparer for the key type. If you're using a custom equality comparer, you can do this with method call syntax.
IEnumerable<bool> matches =
from pair1 in dict1
join pair2 in dict2
on pair1.Key equals pair2.Key
select pair1.Value.Equals(pair2.Value)
bool allValuesMatch = matches.All();
If you require that all items in one dictionary have a matching item in the other, you could do this:
bool allKeysMatch = new HashSet(dict1.Values).SetEquals(dict2.ValueS);
bool dictionariesMatch = allKeysMatch && allValuesMatch;
Well, you could use Enumerable.ElementAt if you really had to, but you shouldn't expect the order to be stable or meaningful. Alternatively, call ToArray or ToList to take a copy.
Usually you only use Values if you're going to iterate over them. What exactly are you trying to do here? Do you understand that the order of entries in a Dictionary<,> is undefined?
EDIT: It sounds like you want something like:
var equal = dict1.Count == dict2.Count &&
dict1.Keys.All(key => ValuesEqual(key, dict1, dict2));
...
private static bool ValuesEqual<TKey, TValue>(TKey key,
IDictionary<TKey, TValue> dict1,
IDictionary<TKey, TValue> dict2)
{
TValue value1, value2;
return dict1.TryGetValue(out value1) && dict2.TryGetValue(out value2) &&
EqualityComparer<TValue>.Default.Equals(value1, value2);
}
EDIT: Note that this isn't as fast as it could be, because it performs lookups on both dictionaries. This would be more efficient, but less elegant IMO:
var equal = dict1.Count == dict2.Count &&
dict1.All(pair => ValuesEqual(pair.Key, pair.Value, dict2));
...
private static bool ValuesEqual<TKey, TValue>(TKey key, TValue value1,
IDictionary<TKey, TValue> dict2)
{
TValue value2;
return dict2.TryGetValue(out value2) &&
EqualityComparer<TValue>.Default.Equals(value1, value2);
}
To add to #JonSkeet's answer, Dictionary<TKey, TValue> is backed by a HashTable, which is an un-ordered data structure. The index of the values is therefore meaningless- it is perfectly valid to get, say, A,B,C with one call and C,B,A with the next.
EDIT:
Based on the comment you made on JS's answer ("I am trying to compare the values in two dictionaries with the same keys"), you want something like this:
public boolean DictionariesContainSameKeysAndValues<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2) {
if (dict1.Count != dict2.Count) return false;
for (var key1 in dict1.Keys)
if (!dict2.ContainsKey(key1) || !dict2[key1].Equals(dict1[key1]))
return false;
return true;
}
You could use an Indexer propertie to lookup the string Key.
It is still not an Index but one more way:
using System.Collections.Generic;
...
class Client
{
private Dictionary<string, yourObject> yourDict
= new Dictionary<string, yourObject>();
public void Add (string id, yourObject value)
{ yourDict.Add (id, value); }
public string this [string id] // indexer
{
get { return yourDict[id]; }
set { yourDict[id] = value; }
}
}
public class Test
{
public static void Main( )
{
Client client = new Client();
client.Add("A1",new yourObject() { Name = "Bla",...);
Console.WriteLine ("Your result: " + client["A1"]); // indexer access
}
}

How do I use HashSet<T> as a dictionary key?

I wish to use HashSet<T> as the key to a Dictionary:
Dictionary<HashSet<T>, TValue> myDictionary = new Dictionary<HashSet<T>, TValue>();
I want to look up values from the dictionary such that two different instances of HashSet<T> that contain the same items will return the same value.
HashSet<T>'s implementations of Equals() and GetHashCode() don't seem to do this (I think they're just the defaults). I can override Equals() to use SetEquals() but what about GetHashCode()? I feel like I am missing something here...
You could use the set comparer provided by HashSet<T>:
var myDictionary = new Dictionary<HashSet<T>, TValue>(HashSet<T>.CreateSetComparer());
digEmAll's answer is clearly the better choice in practice, since it uses built in code instead of reinventing the wheel. But I'll leave this as a sample implementation.
You can use implement an IEqualityComparer<HashSet<T>> that uses SetEquals. Then pass it to the constructor of the Dictionary. Something like the following(Didn't test it):
class HashSetEqualityComparer<T>: IEqualityComparer<HashSet<T>>
{
public int GetHashCode(HashSet<T> hashSet)
{
if(hashSet == null)
return 0;
int h = 0x14345843; //some arbitrary number
foreach(T elem in hashSet)
{
h = unchecked(h + hashSet.Comparer.GetHashCode(elem));
}
return h;
}
public bool Equals(HashSet<T> set1, HashSet<T> set2)
{
if(set1 == set2)
return true;
if(set1 == null || set2 == null)
return false;
return set1.SetEquals(set2);
}
}
Note that the hash function here is commutative, that's important because the enumeration order of the elements in the set is undefined.
One other interesting point is that you can't just use elem.GetHashCode since that will give wrong results when a custom equality comparer was supplied to the set.
You can provide a IEqualityComparer<HashSet<T>> to the Dictionary constructor and make the desired implementation in that comparer.

Problem with custom IComparer for List (sort) - c#

can anyone help, i have problem doing a sort, I thought i had it sorted but appears not to be working.
I have a List which stores the following values
8,6,10,11,7
I also have another List (accessories in my class and it has a propert called accessoryId current the classes are in the order of id which is currenty 6,7,8,10,11)
Hence i need to sort them from 6,7,8,10,11 to the order used from the simple list which is 8,6,10,11,7
I have my icomparable (see below) and i am calling like this - it does enter but something is wrong BECAUSE the list still has all my classes but is still in the order of 6,7,8,10,11
// accesories is the IList<Accessories> (hence why i am use ToList)
// and sortOrder is the simple int list list<int>
accesories.ToList().Sort(new ItemTpComparer(sortOrder));
class ItemTpComparer : IComparer<Accessories>
{
private IList<int> otherList;
public ItemTpComparer(IList<int> otherList)
{
this.otherList = otherList;
}
#region IComparer<Accessories> Members
public int Compare(Accessories x, Accessories y)
{
if (otherList.IndexOf(x.AccessoryId) > otherList.IndexOf(y.AccessoryId))
return 1;
else if (otherList.IndexOf(x.AccessoryId) < otherList.IndexOf(y.AccessoryId))
return -1;
else
return 0;
// tried below also didn't work
//return otherList.IndexOf(x.AccessoryId) - otherList.IndexOf(y.AccessoryId);
The comparer is correct (even the commented single line version). The problem is ToList() creates a new List containing a copy of elements in the IEnumerable<T> object so basically, you are creating a new list, sorting it and throwing it away.
var sortedList = accesories.ToList();
sortedList.Sort(new ItemTpComparer(sortOrder));
for which I'd suggest replacing with:
var sortedList = accessories.OrderBy(sortOrder.IndexOf).ToList();
this way, no comparer implementation would be necessary. You could also sort in the descending order easily:
var sortedList = accessories.OrderByDescending(sortOrder.IndexOf).ToList();
If the object is really List<Accessories>, you could also sort it in place:
((List<Accessories>)accessories).Sort(new ItemTpComparer(sortOrder));
Mehrdad showed you why the list was not sorted. I want to address the performance of the comparer, and also the issue with less sorting items than sorted items.
Using IndexOf on a list to locate the index is quite inefficient. I has to loop through the items in the list to find the right one. Use a dictionary as lookup instead, that way you only loop through the items once:
class ItemTpComparer : IComparer<Accessories> {
private Dictionary<int, int> index;
public ItemTpComparer(IList<int> otherList) {
index = new Dictionary<int, int>();
for (int i = 0; i < otherList.Count; i++) {
index.Add(otherList[i], i);
}
}
public int Compare(Accessories x, Accessories y) {
return index[x.AccessoryId].CompareTo(index[y.AccessoryId]);
}
}
If you want to allow the list of value to sort by to be shorter than the list of items to sort, you check if the value exists in the dictionary:
public int Compare(Accessories x, Accessories y) {
int xIndex, yIndex;
if (!index.TryGetValue(x.AccessoryId, out xIndex)) xIndex = int.MaxValue;
if (!index.TryGetValue(y.AccessoryId, out yIndex)) yIndex = int.MaxValue;
return xIndex.CompareTo(yIndex);
}

What's the fastest way to copy the values and keys from one dictionary into another in C#?

There doesn't seem to be a dictionary.AddRange() method. Does anyone know a better way to copy the items to another dictionary without using a foreach loop.
I'm using the System.Collections.Generic.Dictionary. This is for .NET 2.0.
There's the Dictionary constructor that takes another Dictionary.
You'll have to cast it IDictionary, but there is an Add() overload that takes KeyValuePair<TKey, TValue>. You're still using foreach, though.
There's nothing wrong with a for/foreach loop. That's all a hypothetical AddRange method would do anyway.
The only extra concern I'd have is with memory allocation behaviour, because adding a large number of entries could cause multiple reallocations and re-hashes. There's no way to increase the capacity of an existing Dictionary by a given amount. You might be better off allocating a new Dictionary with sufficient capacity for both current ones, but you'd still need a loop to load at least one of them.
var Animal = new Dictionary<string, string>();
one can pass existing animal Dictionary to the constructor.
Dictionary<string, string> NewAnimals = new Dictionary<string, string>(Animal);
For fun, I created this extension method to dictionary. This should do a deep copy wherever possible.
public static Dictionary<TKey, TValue> DeepCopy<TKey,TValue>(this Dictionary<TKey, TValue> dictionary)
{
Dictionary<TKey, TValue> d2 = new Dictionary<TKey, TValue>();
bool keyIsCloneable = default(TKey) is ICloneable;
bool valueIsCloneable = default(TValue) is ICloneable;
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
{
TKey key = default(TKey);
TValue value = default(TValue);
if (keyIsCloneable)
{
key = (TKey)((ICloneable)(kvp.Key)).Clone();
}
else
{
key = kvp.Key;
}
if (valueIsCloneable)
{
value = (TValue)((ICloneable)(kvp.Value)).Clone();
}
else
{
value = kvp.Value;
}
d2.Add(key, value);
}
return d2;
}
If you're dealing with two existing objects, you might get some mileage with the CopyTo method: http://msdn.microsoft.com/en-us/library/cc645053.aspx
Use the Add method of the other collection (receiver) to absorb them.
I don't understand, why not using the Dictionary( Dictionary ) (as suggested by ageektrapped ).
Do you want to perform a Shallow Copy or a Deep Copy? (that is, both Dictionaries pointing to the same references or new copies of every object inside the new dictionary?)
If you want to create a new Dictionary pointing to new objects, I think that the only way is through a foreach.
For a primitive type dictionary:
public void runIntDictionary()
{
Dictionary<int, int> myIntegerDict = new Dictionary<int, int>() { { 0, 0 }, { 1, 1 }, { 2, 2 } };
Dictionary<int, int> cloneIntegerDict = new Dictionary<int, int>();
cloneIntegerDict = myIntegerDict.Select(x => x.Key).ToList().ToDictionary<int, int>(x => x, y => myIntegerDict[y]);
}
or with an Object that implement ICloneable:
public void runObjectDictionary()
{
Dictionary<int, number> myDict = new Dictionary<int, number>() { { 3, new number(3) }, { 4, new number(4) }, { 5, new number(5) } };
Dictionary<int, number> cloneDict = new Dictionary<int, number>();
cloneDict = myDict.Select(x => x.Key).ToList().ToDictionary<int, number>(x => x, y => myDict[y].Clone());
}
public class number : ICloneable
{
public number()
{
}
public number(int newNumber)
{
nr = newnumber;
}
public int nr;
public object Clone()
{
return new number() { nr = nr };
}
public override string ToString()
{
return nr.ToString();
}
}
The reason AddRange is not implemented on Dictionary is due to the way in which a hashtable (i.e. Dictionary) stores its entries: They're not contiguous in memory as we see in an array or a List, instead they're fragmented across multiple hash buckets, so you cannot block-copy the whole range into a List or you'll get a bunch of empty entries which the Dictionary usually hides from you, the user, through its interface. AddRange assumes a single contiguous range of valid data and can therefore use a fast copy implementation e.g.Array.Copy (like C's memcpy).
Due to this fragmentation, we are left no choice but to iterate through the Dictionary's entries manually in order to extract valid keys and values into a single contiguous List or array. This can be confirmed in Microsoft's reference implementation, where CopyTo is implemented using for.

Categories