I have a set of (totally) ordered objects of class OrderedObject
public class OrderedObject {
public int ord; // ordinal number
}
They have a field ordof type int that is unique for each object and represents their ordinal number. Nonetheless the value of ord can vary for each object. I would like to store the objects in a Dictionary<int*, OrderedObject> such that the key (asterisk stands for pointer to int, not sure how to achieve that with C#, but this is NOT my question) points to the ordinal number of the value object and changes automatically with the latter. Is that somehow possible?
For those who think my undertaking makes little sense: The background is that for now the ordinal number is a natural number represented by an integer, but later it will be replaced by another OrderedObject where ordering works slightly different from ordering the natural numbers.
Note that a key in any dictionary has to be unique so you wont be able to swap by changing one key after the other. in this case you have to implement this differently. but as long as you have unique keys at all times you can do the following:
class ReorderingDictionary : IDictionary<int, OrderItem>
{
private SortedList<int, OrderItem> sortedList = new SortedList<int, OrderItem>();
public void Add(int key, OrderItem value)
{
value.IneedToBeReordered += value_IneedToBeReordered;
sortedList.Add(key, value);
}
void value_IneedToBeReordered(object sender, ReOrderMeEventArgs e)
{
sortedList.Remove(e.OldKey);
OrderItem item = (OrderItem)sender;
sortedList.Add(item.Ord, item);
}
public bool ContainsKey(int key)
{
return sortedList.ContainsKey(key);
}
public ICollection<int> Keys
{
get { return sortedList.Keys; }
}
public bool Remove(int key)
{
return sortedList.Remove(key);
}
public bool TryGetValue(int key, out OrderItem value)
{
return sortedList.TryGetValue(key, out value);
}
public ICollection<OrderItem> Values
{
get { return sortedList.Values; }
}
public OrderItem this[int key]
{
get
{
return sortedList[key];
}
set
{
sortedList[key] = value;
}
}
public void Add(KeyValuePair<int, OrderItem> item)
{
item.Value.IneedToBeReordered += value_IneedToBeReordered;
sortedList.Add(item.Key, item.Value);
}
public void Clear()
{
sortedList.Clear();
}
public bool Contains(KeyValuePair<int, OrderItem> item)
{
return sortedList.ContainsKey(item.Key) && sortedList[item.Key] == item.Value;
}
public void CopyTo(KeyValuePair<int, OrderItem>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { return sortedList.Count; }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(KeyValuePair<int, OrderItem> item)
{
return sortedList.Remove(item.Key);
}
public IEnumerator<KeyValuePair<int, OrderItem>> GetEnumerator()
{
return sortedList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return sortedList.GetEnumerator();
}
}
class OrderItem
{
private int ord;
public int Ord
{
get { return ord; }
set
{
int oldKey = ord;
ord = value;
if (IneedToBeReordered != null)
{
IneedToBeReordered(this, new ReOrderMeEventArgs(oldKey));
}
}
}
public event EventHandler<ReOrderMeEventArgs> IneedToBeReordered;
}
class ReOrderMeEventArgs : EventArgs
{
public int OldKey { get; private set; }
public ReOrderMeEventArgs(int oldKey)
{
this.OldKey = oldKey;
}
}
Related
I'm trying to create a custom control which has a collection of items, but nothing appears on design-time even if I rebuild the project.
(Parents) is custom control
How can I make it appear in the collection editor without losing it after rebuilding the project.
public class ItemsTest : UserControl
{
Parents parent;
ObjectCollections itm ;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ObjectCollections Items { get { return itm; } }
public ItemsTest ()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
itm = new ObjectCollections(this);
parent = new Parents();
}
}
public class ObjectCollections : CollectionBase
{
private List<Parents> _contents;
private ItemsTest itemstest = null;
public ObjectCollections(ItemsTest owner)
{
_contents = new List<Parents>();
this.itemstest = owner;
}
public int IndexOf(Parents item)
{
return _contents.IndexOf(item);
}
public void Insert(int index, Parents item)
{
_contents.Insert(index, item);
}
public void RemoveAt(int index)
{
_contents.RemoveAt(index);
}
public Parents this[int index]
{
get
{
return _contents[index];
}
set
{
_contents[index] = value;
}
}
public void Add(Parents item)
{
_contents.Add(item);
this.itemstest.Controls.Add(item);
this.itemstest.Invalidate();
}
public void Clear()
{
_contents.Clear();
}
public bool Contains(Parents item)
{
if (_contents.Contains(item))
return true;
return false;
}
public void CopyTo(Parents[] array, int arrayIndex)
{
for (int _oj = 0; _oj < Count; _oj++)
{
array.SetValue(_contents[_oj], arrayIndex++);
}
}
public int Count
{
get { return _contents.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(Parents item)
{
return _contents.Remove(item);
}
IEnumerator<Parents> IEnumerable<Parents>.GetEnumerator()
{
//some code
}
IEnumerator IEnumerable.GetEnumerator()
{
//some code
}
}
When I try to add items into the collection editor, rebuild the project, and reopen the collection editor nothing is shown in it.
I'm working on an approach to have a collection that efficiently can search based on more than one property. The sample code of the approach:
class SampleCollection
{
Dictionary<Sample, Sample> _dictItems;
public SampleCollection()
{
_dictItems = new Dictionary<Sample, Sample>(new SampleEqualityComparer());
}
public Sample FindById(int id)
{
return _dictItems[new Sample(id, string.Empty)];
}
public Sample FindByName(string name)
{
return _dictItems[new Sample(-1, name)];
}
}
class Sample
{
public Sample(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public string ALotOfOtherProperties { get; set; }
}
class SampleEqualityComparer : IEqualityComparer<Sample>
{
public bool Equals(Sample x, Sample y)
{
if (x.Id >= 0 && y.Id >= 0)
{
return x.Id == y.Id;
}
return x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase);
}
public int GetHashCode(Sample obj)
{
//try with only name now
return obj.Name.GetHashCode();
//return 0;
}
}
This approach works perfectly fine as long as the Name property is not modified. Understandably, the Hash value no longer matches the original item in the Dictionary when the Name is modified.
Is it possible to force the Dictionary to recalculate the hash of its keys or any other workaround if it is not possible directly.?
It is really a performance hit when using a custom class as a key. The handling can take a 10 times longer.
I suggest that you have a dictionary for name and one for id.
I recommend that you set your Name and Id setter to private like:public string Name { get; private set; }
class SampleCollection
{
public SampleCollection()
{
NameLookup = new Dictionary<string, List<Sample>>();
IdLookup = new Dictionary<int, Sample>();
}
private Dictionary<string, List<Sample>> NameLookup;
private Dictionary<int, Sample> IdLookup;
public void Add(Sample sample)
{
IdLookup.Add(sample.Id, Sample);
List<Sample> list;
if (!NameLookup.TryGetValue(sample.Name, out list))
NameLookup.Add(sample.Name, list = new List<Sample>());
list.Add(Sample);
}
public Sample FindById(int id)
{
Sample result;
IdLookup.TryGetValue(id, out result);
return result;
}
public IEnumerable<Sample> FindByName(string name)
{
List<Sample> list;
if (NameLookup.TryGetValue(name, out list))
foreach(var sample in list)
yield return sample;
}
}
On several recommendations, I started to assign a DataSource to my DataGridView instead of using DataGridView.Rows.Add(...). This is convenient since my data source is already a big list which doesn't change (much). However, when I use the DataSource assignment, it becomes impossible to sort the columns.
class MyGridView : DataGridView
{
private List<Person> m_personList;
private class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string first, string last)
{
FirstName = first;
LastName = last;
}
}
public MyGridView()
{
/* ...initialise stuff... */
m_personList.Add(new Person("Kate", "Smith"));
m_personList.Add(new Person("Bill", "Davids"));
m_personList.Add(new Person("Ann", "Roth"));
this.DataSource = m_personList;
}
}
I also tried to replace List<Person> by BindingList<Person> and by BindingSource, but none of that seems to matter. I also tried adding a custom sorter:
this.SortCompare += MyGridView_SortCompare;
private void MyGridView_SortCompare(object sender, EventArgs e)
{
/* ...Compare method...*/
}
but the thing doesn't even get called. Is there some other way to enable sorting with a DataSource?
Note: Note that my DataSource is not (necessarily) an SQL one, but just any List.
You need to coerce your datasource into a list that supports sorting (IBindingList and IBindingListView), and then it will work out of the box. There are lots of examples on the web, this is the one I've used in the past:
// usage:
// var sortableList = new SortableList(m_personList);
// dgv.DataSource = m_sortableList;
/// <summary>
/// Suitable for binding to DataGridView when column sorting is required
/// </summary>
/// <typeparam name="T"></typeparam>
public class SortableList<T> : BindingList<T>, IBindingListView
{
private PropertyComparerCollection<T> sorts;
public SortableList()
{
}
public SortableList(IEnumerable<T> initialList)
{
foreach (T item in initialList)
{
this.Add(item);
}
}
public SortableList<T> ApplyFilter(Func<T, bool> func)
{
SortableList<T> newList = new SortableList<T>();
foreach (var item in this.Where(func))
{
newList.Add(item);
}
return newList;
}
protected override bool IsSortedCore
{
get { return this.sorts != null; }
}
protected override bool SupportsSortingCore
{
get { return true; }
}
protected override ListSortDirection SortDirectionCore
{
get
{
return this.sorts == null
? ListSortDirection.Ascending
: this.sorts.PrimaryDirection;
}
}
protected override PropertyDescriptor SortPropertyCore
{
get
{
return this.sorts == null ? null : this.sorts.PrimaryProperty;
}
}
public void ApplySort(ListSortDescriptionCollection
sortCollection)
{
bool oldRaise = RaiseListChangedEvents;
RaiseListChangedEvents = false;
try
{
PropertyComparerCollection<T> tmp
= new PropertyComparerCollection<T>(sortCollection);
List<T> items = new List<T>(this);
items.Sort(tmp);
int index = 0;
foreach (T item in items)
{
SetItem(index++, item);
}
this.sorts = tmp;
}
finally
{
RaiseListChangedEvents = oldRaise;
ResetBindings();
}
}
public bool Exists(Predicate<T> func)
{
return new List<T>(this).Exists(func);
}
string IBindingListView.Filter
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
void IBindingListView.RemoveFilter()
{
throw new NotImplementedException();
}
ListSortDescriptionCollection IBindingListView.SortDescriptions
{
get { return (this.sorts == null ? null : this.sorts.Sorts); }
}
bool IBindingListView.SupportsAdvancedSorting
{
get { return true; }
}
bool IBindingListView.SupportsFiltering
{
get { return false; }
}
protected override void RemoveSortCore()
{
this.sorts = null;
}
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
ListSortDescription[] arr = { new ListSortDescription(prop, direction) };
ApplySort(new ListSortDescriptionCollection(arr));
}
}
public class PropertyComparerCollection<T> : IComparer<T>
{
private readonly PropertyComparer<T>[] comparers;
private readonly ListSortDescriptionCollection sorts;
public PropertyComparerCollection(ListSortDescriptionCollection
sorts)
{
if (sorts == null)
{
throw new ArgumentNullException("sorts");
}
this.sorts = sorts;
List<PropertyComparer<T>> list = new
List<PropertyComparer<T>>();
foreach (ListSortDescription item in sorts)
{
list.Add(new PropertyComparer<T>(item.PropertyDescriptor,
item.SortDirection == ListSortDirection.Descending));
}
this.comparers = list.ToArray();
}
public ListSortDescriptionCollection Sorts
{
get { return this.sorts; }
}
public PropertyDescriptor PrimaryProperty
{
get
{
return this.comparers.Length == 0
? null
: this.comparers[0].Property;
}
}
public ListSortDirection PrimaryDirection
{
get
{
return this.comparers.Length == 0
? ListSortDirection.Ascending
: this.comparers[0].Descending
? ListSortDirection.Descending
: ListSortDirection.Ascending;
}
}
int IComparer<T>.Compare(T x, T y)
{
int result = 0;
foreach (PropertyComparer<T> t in this.comparers)
{
result = t.Compare(x, y);
if (result != 0)
{
break;
}
}
return result;
}
}
public class PropertyComparer<T> : IComparer<T>
{
private readonly bool descending;
private readonly PropertyDescriptor property;
public PropertyComparer(PropertyDescriptor property, bool descending)
{
if (property == null)
{
throw new ArgumentNullException("property");
}
this.descending = descending;
this.property = property;
}
public bool Descending
{
get { return this.descending; }
}
public PropertyDescriptor Property
{
get { return this.property; }
}
public int Compare(T x, T y)
{
int value = Comparer.Default.Compare(this.property.GetValue(x),
this.property.GetValue(y));
return this.descending ? -value : value;
}
}
An alternate is to convert your List into DataTable and then bind that DataTable to the DataGridView through BindingSource. This way your DataGridView will inherit the sort options available with the DataTable.
For converting a list to DataTable, refer the article:
How to fill a datatable with List<T>
This question already has answers here:
Getting hash of a list of strings regardless of order
(5 answers)
Closed 8 years ago.
Let's say I have a class
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
}
and I have a collection class that is simply a strongly typed List
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base (enumerable) {}
}
I want MyClassList to be able to generate a unique hash-code for MyClassList based on the contents. The hash-code of MyClass should be based on both properties. The hash-code of MyClassList should be the same even if the order of the objects is different.
To handle the ordering issue I was thinking I could order the list before generating the hash-code, but I'm not sure how to generate the hash-code of the list.
For optimal performance I would try to avoid iterating the whole collection every time GetHashCode is called. The purpose of GetHashCode is to improve performance to a point better than evaluating every element. So I might try maintaining the hash code when elements in the list are changed like this.
class Program
{
static void Main(string[] args)
{
MyClassList l = new MyClassList() { new MyClass() {Type="Bob", Id=1}, new MyClass() {Type="Jones", Id=2}};
MyClassList l2 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }, new MyClass() { Type = "Bob", Id = 1 } };
MyClassList l3 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }};
Console.WriteLine("{0} {1} {2}", l.GetHashCode(), l2.GetHashCode(), l3.GetHashCode());
l3.Add(new MyClass() { Type = "Bob", Id = 1 });
Console.WriteLine("{0}", l3.GetHashCode());
}
}
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
return (Type.GetHashCode() % 0x8000) | (int)((uint)Id.GetHashCode() & 0xFFFF0000);
}
}
public class MyClassList : IList<MyClass>
{
List<MyClass> internalList;
int hashCode = 0;
public MyClassList()
{
internalList = new List<MyClass>();
}
private void IncludeInHash(MyClass item)
{
hashCode ^= item.GetHashCode();
}
private void ExcludeFromHash(MyClass item)
{
IncludeInHash(item);
}
public override int GetHashCode()
{
return hashCode;
}
public int IndexOf(MyClass item)
{
return internalList.IndexOf(item);
}
public void Insert(int index, MyClass item)
{
internalList.Insert(index, item);
// Make sure Insert is successful (doesn't throw an exception) before affecting the hash
IncludeInHash(item);
}
public void RemoveAt(int index)
{
MyClass reduce = internalList[index];
internalList.RemoveAt(index);
// Make sure RemoveAt is successful before affecting the hash
ExcludeFromHash(reduce);
}
public MyClass this[int index]
{
get
{
return internalList[index];
}
set
{
MyClass reduce = internalList[index];
internalList[index] = value;
// Make sure these happen atomically; don't allow exceptions to prevent these from being accurate.
ExcludeFromHash(reduce);
IncludeInHash(value);
}
}
public void Add(MyClass item)
{
internalList.Add(item);
IncludeInHash(item);
}
public void Clear()
{
internalList.Clear();
hashCode = 0;
}
public bool Contains(MyClass item)
{
return internalList.Contains(item);
}
public void CopyTo(MyClass[] array, int arrayIndex)
{
internalList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return internalList.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(MyClass item)
{
if (internalList.Remove(item))
{
ExcludeFromHash(item);
return true;
}
else
return false;
}
public IEnumerator<MyClass> GetEnumerator()
{
return internalList.AsReadOnly().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
The solution given by clto works. Here is an alternative: sort the list by some total ordering (any ordering will do, as long as it is unambiguous). Then you can calculate the hash code using any normal means. You don't need order-independence. You could even use a cryptographic hash function.
I propose this solution (I didn't implement the Equals method) :
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
int hash = 17;
hash = hash + 23 * this.Type.GetHashCode();
hash = hash + 23 * this.Id.GetHashCode();
return hash;
}
}
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base(enumerable) { }
public override int GetHashCode()
{
return this.Aggregate(17, (state, current) => state * 23 + current.GetHashCode());
}
}
The way to generate the hashcode is inspired from Microsoft method to compute the hash value for anonymous objects.
If the order isn't important then you should use a collection that inherently is a set, rather than a list.
Also, it's generally best to not inherit from collections; use composition instead.
So for a collection you can use a HashSet, as it will have set semantics.
To have MyClass use both properties as it's identity just override it's equals and get hash code implementations, or create an IComparer<MyClass> if you can't or don't want to do that.
public class MyClass:IEquatable<MyClass>
{
public string Type { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as MyClass);
}
public bool Equals(MyClass other)
{
if (other == null)
return false;
return Type == other.Type &&
Id == other.Id;
}
public override int GetHashCode()
{
return Type.GetHashCode() * 79 + Id;
}
}
Then your collection is as simple as:
HashSet<MyClass> set = new HashSet<MyClass>();
And if you want to compare various sets just use:
HashSet<MyClass>.CreateSetComparer();
This is a entity and i want to list all the children node for a given node in a generic function
public static List<T> BuildTree<T>(List<T> list, T selectNode string keyPropName, string parentPropName, string levelPropName, int level = 0)
{
List<T> entity = new List<T>();
foreach (T item in list)
{
}
return entity;
}
example of the entity structure
protected long _coakey;
protected long _parentkey;
protected string _coacode;
protected string _coacodeclient;
protected string _coaname;
protected int _coalevel;
[DataMember]
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChnaged(); }
}
[DataMember]
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChnaged(); }
}
[DataMember]
public string coacode
{
get { return _coacode; }
set { _coacode = value; this.OnChnaged(); }
}
[DataMember]
public string coacodeclient
{
get { return _coacodeclient; }
set { _coacodeclient = value; this.OnChnaged(); }
}
[DataMember]
public string coaname
{
get { return _coaname; }
set { _coaname = value; this.OnChnaged(); }
}
[DataMember]
public int coalevel
{
get { return _coalevel; }
set { _coalevel = value; this.OnChnaged(); }
}
Your BuildTree<T> method cannot determine the structure of the tree unless it knows something about its structure. At a very minimum, I would suggest making a base class or interface that defines a tree node, and then change the BuildTree method to work specifically with those types of objects. Then, it will be able to figure out the tree structure. Each of you entity classes would have to implement that tree node interface or inherit from the tree node base class. For instance:
public abstract class TreeNodeBase
{
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChanged(); }
}
protected long _parentkey;
}
public class MyEntityTreeNode : TreeNodeBase
{
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChanged(); }
}
protected long _coakey;
// etc...
}
// Note the generic type constraint at the end of the next line
public static List<T> BuildTree<T>(List<T> list, T selectNode, string keyPropName, string parentPropName, string levelPropName, int level) where T : TreeNodeBase
{
List<T> entity = new List<T>();
foreach (TreeNodeBase node in list)
{
long parentKey = node.parentkey;
// etc...
}
return entity;
}
Node class:
public class Node<TKey, TValue> where TKey : IEquatable<TKey>
{
public TKey Key { get; set; }
public TKey ParentKey { get; set; }
public TValue Data { get; set; }
public readonly List<Node<TKey, TValue>> Children = new List<Node<TKey, TValue>>();
}
TreeBuilder:
public static Node<TKey, TValue> BuildTree<TKey, TValue>(IEnumerable<Node<TKey, TValue>> list,
Node<TKey, TValue> selectNode)
where TKey : IEquatable<TKey>
{
if (ReferenceEquals(selectNode, null))
{
return null;
}
var selectNodeKey = selectNode.Key;
foreach (var childNode in list.Where(x => x.ParentKey.Equals(selectNodeKey)))
{
selectNode.Children.Add(BuildTree(list, childNode));
}
return selectNode;
}
Usage:
List<MyClass> list = ...
var nodes = list.Select(x => new Node<long, MyClass>
{
Key = x.MyKey,
ParentKey = x.MyParentKey,
Data = x
}).ToList();
var startNode = nodes.FirstOrDefault(x => x.Data.Stuff == "Pick me!");
var tree = BuildTree(nodes, startNode);
MyClass example:
public class MyClass
{
public long MyKey;
public long MyParentKey;
public string Name;
public string Text;
public string Stuff;
}
I have solved it my self hope it help you
public static List<T> BuildTree<T>(List<T> list, T selectedNode, string keyPropName, string parentPropName, int endLevel = 0, int level = 0)
{
List<T> entity = new List<T>();
Type type = typeof(T);
PropertyInfo keyProp = type.GetProperty(keyPropName);
string _selectedNodekey = keyProp.GetValue(selectedNode, null).ToString();
PropertyInfo parentProp = type.GetProperty(parentPropName);
foreach (T item in list)
{
string _key = keyProp.GetValue(item, null).ToString();
string _parent = parentProp.GetValue(item, null).ToString();
if (_selectedNodekey == _parent)
{
T obj = (T)Activator.CreateInstance(typeof(T));
obj = item;
entity.Add(obj);
if (level == endLevel && level!=0) break;
entity.AddRange(BuildTree<T>(list, obj, keyPropName, parentPropName, level + 1));
}
}
return entity;
}