Sorting ListView by date of items - c#

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>();

Related

C# Most elegant way to convert string array to dictionary with different keys

I have a collection of string arrays (result of splitting CSV to rows and then delimiting)
{31001, 2014-01-01, 24:00:00},{31001, 2014-01-02, 24:00:00},{31001, 2014-01-03, 24:00:00} ...
Now I need to convert it to list of dictionaries, where each dictionary element will have custom key depending on arrays element index. So I want to achieve something like this:
{
{"Index":"31001", "Date":"2014-01-01", "Time":"24:00:00"},
{"Index":"31001", "Date":"2014-01-02", "Time":"24:00:00"},
{"Index":"31001", "Date":"2014-01-03", "Time":"24:00:00"},
...
}
I can't use models for this
Assuming you will have duplicate keys you could use something like
List<KeyValuePair<string,Tuple<string,string>>
where the key is the Index and the rest of the data belongs in the Tuple (Date,Time,..etc).
If you will have more data you couls simply use a List<KeyValuePair<string,List<string>>>
You could do it with an anonymous object.
this way would result in objList having all the rows with the properties Index,Date and Time. whilst not using named classes.
(please note I'm still learning if I don't name things right)
List<Object> objList = new List<object>();
foreach(var line in lines)
{
var fields = line.Split(',');
objList.Add(new {
Index = fields[0],
Date = fields[1],
Time=fields[2]
});
}

Can a ComboBox accommodate both numbers and text?

I am trying to figure out something. I have a method which adds some items into a ComboBox named "cbSize". I realize that if I add two types of data into it, the code will crash. Is this because a ComboBox can only accommodate one type of data?
items.Add(1);
items.Add(10);
items.Add(100);
items.Add(2);
items.Add(20);
items.Add(3);
items.Add(30); //works fine if add numbers only
//items.Add("4"); //will crash if mix both numbers and text
//items.Add("2"); //works fine if add text only
//then sort them out
items.Sort();
//now clear original cbSize items
cbSize.Items.Clear();
//and add them back in sorted order
cbSize.Items.AddRange(items.ToArray());
//gotta clear ArrayList for the next time or else things will add up
items.Clear();
Is this because a ComboBox can only accommodate one type of data?
No, try below it will work
cbSize.Items.Add("44");
cbSize.Items.Add(44);
problem is with your items collection, it is type safe. you can't add different types to it.
try with list of objects. it will work. reason is both int and string are objects
List<object> items = new List<object>();
items.Add(1);
items.Add(30);
items.Add("4");
items.Add("2");
//since you have string and int value you need to create custom comparer
items.Sort((x, y) => Convert.ToInt32(x).CompareTo(Convert.ToInt32(y)));
//now clear original cbSize items
cbSize.Items.Clear();
//and add them back in sorted order
cbSize.Items.AddRange(items.ToArray());
OR you can use ArrayList class (not type-safe because it can store any object)
var integers = new ArrayList();
integers.Add(1);
integers.Add(2);
integers.Add("3");
comboBox1.Items.AddRange(integers.ToArray());
What is type-safe in .net?
Yes. What you can do is to provide a Size class that will adapt from ints and strings:
items.Add(new Size(3));
items.Add(new Size(4));
items.Add(new Size("large"));
Then, you could make the Size class implement IComparable so you can call the Sort() method.

Correct Collection to use for ordered collection

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

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;

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