Correct Collection to use for ordered collection - c#

I have a collection, That needs to be ordered in the order it is created.
But then at any time The User can change the order (ie move the 4th item to the first postion)
Is there any Collections with pre-built methods?
or should I use a SortedList.
Add(key++, Object); //pseudo code
then to change item
SwapObject(int key, int SwapKey)
{
where key == value
tempvalue = key;
SwapKey = key;
key = tempvalue;
}

You can use a generic List<> which has the Insert method, so you can insert an object in a given position any time.

You can use a simple List<YourObject> as container and implement IComparer for sorting.
List also provides methods for sorting, insert at a location or remove from a location

Related

How to properly use SortedDictionary in c#?

I'm trying to do something very simple but it seems that I don't understand SortedDictionary.
What I'm trying to do is the following:
Create a sorted dictionary that sorts my items by some floating number, so I create a dictionary that looks like this
SortedDictionary<float, Node<T>> allNodes = new SortedDictionary<float, Node<T>>();
And now after I add items, I want to remove them one by one (every removal should be at a complexity of O(log(n)) from the smallest to the largest.
How do I do it? I thought that simply allNodes[0] will give me the the smallest, but it doesn't.
More over, it seems like the dictionary can't handle duplicate keys. I feel like I'm using the wrong data structure...
Should I use something else if I have bunch of nodes that I want to be sorted by their distance (floating point)?
allNodes[0] will not give you the first item in the dictionary - it will give you the item with a float key value of 0.
If you want the first item try allNodes.Values.First() instead. Or to find the first key use allNodes.Keys.First()
To remove the items one by one, loop over a copy of the Keys collection and call allNodes.Remove(key);
foreach (var key in allNodes.Keys.ToList())
{
allNodes.Remove(key);
}
To answer your addendum to your question, yes SortedDictionary (any flavor of Dictionary for that matter) will not handle duplicate keys - if you try and add an item with an existing key it will overwrite the previous value.
You could use a SortedDictionary<float, List<Node<T>>> but then you have the complexity of extracting collections versus items, needing to initialize each list rather than just adding an item, etc. It's all possible and may still be the fastest structure for adds and gets, but it does add a bit of complexity.
Yes, you're right about complexity.
In SortedDictionary all the keys are sorted. If you want to iterate from the smallest to the largest, foreach will be enough:
foreach(KeyValuePair<float, Node<T>> kvp in allNodes)
{
// Do Something...
}
You wrote that you want to remove items. It's forbidden to remove from collections during iteratation with foreach, so firstly create a copy of it to do so.
EDIT:
Yes, if you have duplicated keys you can't use SortedDictionary. Create a structural Node with Node<T> and float, then write a comparer:
public class NodeComparer : IComparer<Node>
{
public int Compare(Node n1, Node n2)
{
return n2.dist.CompareTo(n1.dist);
}
}
And then put everything in simple List<Node> allNodes and sort:
allNodes.Sort(new NodeComparer());
As a Dictionary<TKey, TValue> must have unique keys, I'd use List<Node<T>> instead. For instance, if your Node<T> class has a Value property
class Node<T>
{
float Value { get; set; }
// other properties
}
and you want to sort by this property, use LINQ:
var list = new List<Node<T>>();
// populate list
var smallest = list.OrderBy(n => n.Value).FirstOrDefault();
To remove the nodes one by one, just iterate threw the list:
while (list.Count > 0)
{
list.RemoveAt(0);
}

Sorting ListView by date of items

I have a ListView to show some images (using ImageList). Everything works fine so far. I can sort items by theire Text property easily. But I want to be able to sort them by theire creation date as well (ASC or DESC).
Items are created like this:
Item.Text = file name
item.Tag = file path
I am trying to sort the date like this but no success in sorting the dictionary, and also I have no idea how to cast that dictionary items back into listview:
private void menuViewSortDate_Click(object sender, EventArgs e)
{
var files = new Dictionary<ListViewItem, DateTime>();
foreach(ListViewItem item in listViewLoadedImages.Items)
{
files.Add(item, File.GetCreationTime(item.Tag.ToString()));
}
listViewLoadedImages.BeginUpdate();
listViewLoadedImages.Items.Clear();
// how to sort dictionary by DateTime ??
// like files.Sort(bydate);
//How can refer to key values (listviewitems) in dictionary
listViewLoadedImages.Items.AddRange((ListViewItem)files.Keys);
listViewLoadedImages.EndUpdate();
}
A Dictionary is optimized to be quick to retrieve, but that means the internal order is undefined, and specified by the internal implementation.
If you need to have a specific order for keys, use the SortedDictionary class, passing it an IComparer, if necessary, to define the sorting order.
As Avner says a dictionary is not ordered by default however you could use some LINQ to return a IEnumerable by doing:
var results = files.OrderBy(x=>x.Value);
I'm also not sure how you can make a ListViewItem unique as your key however you could try and cast your dictionary keys
var lvItems = files.Keys.Cast<ListViewItem>();

Can I reference an object within a collection by it's name?

I have a Collection object (based on System.Collections.CollectionBase) but to access the values of objects within that collection, I have to use the index currently. Is it possible to get the values based on the name of the object within the collection?
For example, instead of...
MyCollection[0].Value
... how can I do something along the lines of:
MyCollection["Birthday"].Value
In order to do this you would need to have a Dictionary<string,object>. Unfortunately collections only allow random access by index.
You could do something like this:
var item = MyCollection
.Where(x => x.SomeProp == "Birthday")
.FirstOrDefault();
// careful - item could be null here
var value = item.Value;
But this will be nowhere near as efficient as random access by index.
You could use a Dictionary<TKey, TValue> which allows you to access its element by a key. So if the key in your example is a string you could use Dictionary<string, TValue>.
Why do you think objects in a collection have names? They don't. What you can do is use a Dictionary<String, SomethingElse> to enable your syntax.
As others has said, you need a Dictionary<> to do that. If you cannot change the code that provides the collection, you can use LINQ's ToDictionary() method to convert it to a dictionary yourself:
var dict = MyCollection.ToDictionary(obj => obj.Name);
From there on, you can do:
var value = dict["Birthday"].Value;
You could use the this[] accessor
public Item this[string name]
{
get
{
// iterate through the elements of the collection
//and return the one that matches with name
}
}
Have this getter property on your MyCollectionClass
One workaround could be
private const int BIRTHDAY = 0;
var value = MyCollection["Birthday"].Value;

SortedSet and SortedList fails with different enums

The whole story; I have some KeyValuePairs that I need to store in a session and my primary goal is to keep it small. Therefore I don't have the option of using many different collection. While the key is a different enum value of of a different enum type the value is always just a enum value of the same enum type. I have chosen a HashTable for this approach which content look like this (just many more):
// The Key-Value-Pairs
{ EnumTypA.ValueA1, MyEnum.ValueA },
{ EnumTypB.ValueB1, MyEnum.ValueB },
{ EnumTypC.ValueC1, MyEnum.ValueA },
{ EnumTypA.ValueA2, MyEnum.ValueC },
{ EnumTypB.ValueB1, MyEnum.ValueC }
At most I am running contains on that HashTable but for sure I also need to fetch the value at some point and I need to loop through all elements. That all works fine but now I have a new requirement to keep the order I have added them to the HashTable -> BANG
A HashTable is a map and that is not possible!
Now I thought about using a SortedList<object, MyEnum> or to go with more Data but slightly faster lookups and use a SortedSet<object> in addition to the HashTable.
Content below has been edited
The SortedList is implemented as
SortedList<Enum, MyEnum> mySortedList = new SortedList<Enum, MyEnum>();
the SortedSet is implemented as
SortedSet<Enum> mySortedSet = new SortedSet<Enum>();
The described Key - Value - Pairs are added to the sorted list with
void AddPair(Enum key, MyEnum value)
{
mySortedList.Add(key, value);
}
And for the SortedSett like this
void AddPair(Enum key)
{
mySortedSet.Add(key);
}
Both are failing with the exception:
Object must be the same type as the
enum
My question is: What goes wrong and how can I archive my goal?
Used Solution
I've decided to life with the downside
of redundant data against slower
lookups and decided to implement a
List<Enum> which will retain the
insert order parallel to my already
existing HashTable.
In my case I just have about 50-150
Elements so I decided to benchmark the
Hashtable against the
List<KeyValuePair<object,object>>
Therefore I have create me the
following helper to implement
ContainsKey() to the
List<KeyValuePair<object,object>>
static bool ContainsKey(this List<KeyValuePair<object, object>> list, object key)
{
foreach (KeyValuePair<object, object> p in list)
{
if (p.Key.Equals(key))
return true;
}
return false;
}
I inserted the same 100 Entries and
checked randomly for one of ten
different entries in a 300000 loop.
And... the difference was tiny so I
decided to go with the
List<KeyValuePair<object,object>>
I think you should store your data in an instance of List<KeyValuePair<Enum, MyEnum>> or Dictionary<Enum, MyEnum>.
SortedSet and SortedList are generic, but your keys are EnumTypeA/EnumTypeB, you need to specify the generic T with their base class(System.Enum) like:
SortedList<Enum, MyEnum> sorted = new SortedList<Enum, MyEnum>();
EDIT
Why you got this exception
SortedList and SortedSet use a comparer inside to check if two keys are equal. Comparer<Enum>.Default will be used as the comparer if you didn't specify the comparer in the constructor. Unfortunately Comparer<Enum>.Default isn't implemented as you expected. It throws the exception if the two enums are not the same type.
How to resolve the problem
If you don't want to use a List<KeyValuePair<Enum, MyEnum>> and insist using SortedLIst, you need to specify a comparer to the constructor like this:
class EnumComparer : IComparer<Enum>
{
public int Compare(Enum x, Enum y)
{
return x.GetHashCode() - y.GetHashCode();
}
}
var sorted = new SortedList<Enum, MyEnum>(new EnumComparer());
Btw, I think you need to obtain the "inserting order"? If so, List<KeyValuePair<K,V>> is a better choice, because SortedSet will prevent duplicated items.

How could I determine if an array contains a value at a particular index?

I have an array with values at meaningful indices. How can I tell if a particular there is a value at a particular element?
Array.Exists() is the closest I've found, but it looks overcomplicated for what I want, so I'm curious to know if it's really the best way.
UPDATE
OK, so I have an array of objects:
ImageGroup[] Images;
And the index of the elements corresponds to a feature of that item. In this case, the index refers to a value within the filename of the original image. When I come across a filename, I want to check if an element exists at the corresponding index and create one if not.
So I want to know if Images[someInt] exists.
Updated
With the last update this looks more like a dictionary (unless you're going in numerical order and not where "1,2,5" may have been populated, but 3,4 are absent and need to be created). If this is something where index could potentially skip, I would recommend a dictionary:
Dictionary<Int32,Image> images = new Dictionary<Int32, Image>();
// populated previously
Int32 needle = GetIndexOfImage(newImage);
if (!images.ContainsKey(needle))
images.Add(needle, newImage);
Then, once you're done populating, you can then re-reference the item by index in the following fashion:
images[specificIndex]
Once more, you can retrieve all the elements stored using the following as well:
images.Values
Some resources:
Dictionary
Dictionary.ContainsKey
First response:
if (a[index] == interesting) ....
After the Edit(s):
int index = GetIndexFromFilename(filename);
// if (Images[index] != null && Images[index] == interesting) ....
if (Images[index] == null)
Images[index] = CreateImage(filename);
But you should probably just use a Dictionary<string, Image> and use filename as the Key.
It sounds like what you're looking for is the functionality of a dictionary. It would be extremely helpful if you posted how you're populating your array, and how you want to be able to index it. From what I can gather, this is how I would implement...
Dictionary<SomeEnum, ImageGroup> images = new Dictionary<SomeEnum, ImageGroup>();
foreach(SomeEnum enumValue in Enum.GetValues(typeof(SomeEnum)))
{
ImageGroup group = BuildImageGroup();
images.Add(enumValue, group);
}
then you can do:
...
if(images.ContainsKey(SomeEnum.SomeValue))
return images[SomeEnum.SomeValue];
else
return DoSomethingFancy();
If you have multiple image groups for a single enum value (collisions), then you can use a collection of ImageGroups in the dictionary, like this:
Dictionary<SomeEnum, ImageGroup[]>

Categories

Resources