Re-ordering collection C# - c#

I have a problem which I cant seem to find answer to through searches (either that or I am searching for the completely wrong thing!). I have a list of items called "Top 10" in a sortedlist item that is populated from my DB (SortedList where int is position and string is item). I want to be able to move items up & down the list order at the click of a button and then save the new order back to the DB. I am OK with the DB part it is just the re-ordering I am really struggling with - is a sortedlist the correct collection for this?
Many thanks for any advice offered.

A SortedList is for maintaining order within your SortedList as you add or remove items from it.
You should create a custom list of your objects and then sort on property of that object.
So if your entry in your database was like this you could place it in an object, add it to a list and then sort it using Lambda on which ever criteria you like
public class LeaguePosition
{
public int Position { get; set; }
public string Team { get; set; }
}
List<LeaguePosition> League = new List<LeaguePosition>();
League.Add(new LeaguePosition() { Position = 2, Team = "Wolves" });
League.Add(new LeaguePosition() { Position = 3, Team = "Spurs" });
League.Add(new LeaguePosition() { Position = 1, Team = "Villa" });
League.Sort((teamA, teamB) => teamA.Position.CompareTo(teamB.Position));
You can also then use RemoveAt() and Insert() to move items about to custom positions within the list.
LeaguePosition teamToMove = League[1];
League.RemoveAt(1);
League.Insert(2, teamToMove);

No, a SortedList will keep things in sorted (alpha/numeric) order. You want a plain list that will let you pull things out and insert them at different positions.

A SortedList is going to force each item to keep track of its position within the list relative to the other items (your int position). This should be a responsiblity of the Collection, so you should use some sort of Collection that will allow you to move things up/down/around without having to manually correct each item.

I would say a SortedList is exactly what you want. When you get data back from the database you want to keep it in order based on the position field (what I can tell from your OP). I'm assuming you will have a class that contains the data you're getting back from the DB. To keep the data in the right order you will need to implement the IComparable interface in your custom class so that the SortedList knows what values to use to keep the list in order. This way as you add/remove items you will keep them in the right order.
You could use a generic List<> but then you have to write all the code to sort your items yourself, the SortedList already does this so why re-invent the wheel?
Take a look at this page for more info:
http://www.dotnetspider.com/resources/4679-Applying-custom-sort-SortedList.aspx

Related

Reflection: Get properties/fields from individual elements of a List<>

I'm sorry in advance for the mess you're about to read, because I'm not 100% sure what I'm searching for.
I have created an entire UI system that automatically grabs a list of properties from various scripts/components on GameObjects (Unity) and creates a fitting UI input variant for them (for example, float gets a single line, Vector3 gets 3 lines, color gets something else etc.).
What goes into UI input fields creation is a Component (that we want to look into), while individual created UI inputs store this Component and Property Name. So when input changes in one of input fields, it does SetValue on Property of a Component. Now I have also created a variant where we peak into a Class of a property and basically list Property's Properties, so the UI input stores Component, Property Name, and subProperty's Name and modifies properties as such. All this works well.
So, now I hit a brick wall with Lists. I would like to treat individual elements of a list as properties so that I could pass them into my preexisting UI scheme.
tl;dr Does List<> treat it's individual elements as Properties, Fields or does it depend on the situation? How do I get these properties, fields or their respective names from this list in order to use them with my mess of an UI system? 0 work for me means treating individual elements of List as properties.
----------------------------
EDIT----------------------------
Again I am sorry for this mess of a question. It is a mixture of confused theory and description of an existing situation that I am trying to shoehorn into my already existing project (which is a bit too over the place to be easily reduced to an example).
If anyone grasped what I was asking for, the single easiest solution was to create a property which prior to listing was equated to an element of a list.
Example looks something like this:
public List<MyCustomClass> myCustomList;
[Listable("ForEdit")]
public myCustomClass myCustomPropertyForEdit
{
get;
set;
}
And before withdrawing properties of myCustomPropertyForEdit's class (myCustomClass) I would simply do:
myCustomPropertyForEdit = myCustomList[0]; //or whatever index is in question
Then later on I would do reflection on "myCustomPropertyForEdit" instead of myCustomList. I highly doubt this will ever help anyone because it touches more onto how I built methods for listing properties into my UI, but there it is just in case.
List stores references to objects, by providing an index you get a standard object reference, which you can proceed to query using reflection (do not do it against the list itself as you will get methods of the List class, and notthing related to what the list contains)
take for example:
public Class Tree
{
public int branches;
public Tree(int branch)
{
branches=branch;
}
}
List<Tree> trees = new List<Tree>();
trees.Add(new Tree(3));
now my list has one element at index 0;
so i can do trees[0].branches;
to access the fields/props of an element in trees.
list is not an array, it holds the actual item, allowing you to reference, not just the object but also its own unique variables. as long as they are public in the class.

Linq to get list of custom objects where a certain value contained within dictionary property equals a specific value

I am struggling to solve this issue and have searched multiple ways and cannot seem to find an answer. I inherited this app from someone else and need to add a couple features to the app. I have not worked much with dictionaries and linq before, so I have been searching and trying to gain knowledge to do what I need to do.
There is a class with the following properties(removed some properties not necessary for this discussion):
class EmailRecord
{
public Dictionary<string, List<string>> emails = new Dictionary<string, List<string>>();
public string RecordID { get; set; }
[followed by additional properties and constructors...]
When the objects are created, the emails Property would have a template string in the key, and a list of strings containing email addresses in the values. For my purposes, I do not need to know what is in the key.
I have a list of EmailRecord objects called allRecords. I need to query allRecords to get a list of all EmailRecord objects where the emails dictionary property's list of values contains a specific email address I have stored in a variable called recipientEmail. The key doesn't matter, and it doesn't matter how many times the email shows up. I just need the instance of the object included in the results if the email shows up anywhere in the values of the emails property. In an instance of EmailRecord, the emails dictionary property may have two keys and within each of those keys, multiple emails in a list of strings for the value. I don't need to limit to a specific key, I just need to know if an email exists anywhere within the list of email strings anywhere in that dictionary.
I've tried a few things, with the latest being this (which doesn't work):
var results = EmailRecords
.SelectMany(x => x.emails)
.Where(x => x.Value.Contains(recipientEmail));
The above just seems to be returning the dictionary property, not the entire object.
I want to be able to loop through the results with something like this:
foreach (EmailRecord foundRecord in results) {
...do work here
}
Any thoughts or suggestions to assist me as I am trying to learn Linq? Thank you in advance for any help you can provide.
If you want to loop through EmailRecord objects which one of its emails property values contains recipientEmail, then you need to have a list of EmailRecord first. Then search throught them. following should do the trick.
List<EmailRecord> EmailRecords = new List<EmailRecord>();
//Fill the EmailRecords somewhere
foreach (KeyValuePair<string, List<string>> emailfoundRecord in
EmailRecords.emails.Where(x => x.Value.Contains(recipientEmail)))
{
//do work here
}
When you call EmailRecords.SelectMany(x => x.Emails) what you get back is an IEnumerable<KeyValuePair<string, List<string>>> or similar. Obviously this is not what you're after for your result since it strips away all that other information.
With LINQ the first thing to consider at each stage is what you are expecting to get out of the query. In this case the query results should be an enumeration of EmailRecord instances which is also what we're feeding in. Filtering that list is most simply done with the Where method, so that's where you should do all the work
Next decide on your filter criteria and write the filter predicate to suit. For any given EmailRecord we want to find out if any of the dictionary entries contains a particular email address. Since the dictionary values are lists we'll use Contains to do the actual comparison, and Any to test the dictionary itself.
Which looks like this:
var filtered = EmailRecords.Where(e =>
e.Emails.Any(kv =>
kv.Value.Contains(recipientEmail)
)
);
This works because a dictionary is also an enumerable, with each entry in the enumeration being a key/value pair.
Using Any will stop when it finds a single matching entry instead of continuing to the end of the Emails dictionary for every EmailRecord instance. If there are a lot of emails and you're expecting a high number of selections then this might save some time. Probably not however, since generally this sort of structure doesn't have a lot of duplicate email addresses in it.
Depending on how often you want to do this however it might be quicker to build a lookup and query that. Assuming that your EmailRecords list changes infrequently and you are doing a lot of this sort of lookup, you could get a large speedup.
I'll use a Dictionary<string, EmailRecord[]> for the lookup because it's (fairly) simple to build once we get a list of all of the pairs of email address and EmailRecord objects:
var emailReferences = EmailRecords.SelectMany(e =>
e.Emails.SelectMany(kv =>
kv.Value.Select(v =>
new { address = v, record = e }
)
)
);
var lookup =
emailReferences
.GroupBy(i => i.address, i => i.record)
.ToDictionary(g => g.Key, g => g.ToArray());
;
From this you will be able to locate an email address and get its referencing EmailRecord instances fairly simply and quickly:
EmailRecord[] filtered = null;
lookup.TryGetValue(recipientEmail, out filtered);
This will be faster per lookup than the LINQ equivalent above, but the setup could consume a fair amount of time and memory for large lists. If you have small or frequently changing lists (since the lookup has to be regenerated or at least invalidated at each change) then this won't improve your program's speed.
As a completely unsolicited aside, here's an extension method I use when dealing with dictionaries that have List<> as the value:
public static partial class extensions
{
public static Dictionary<TKey, List<TElem>> Add<TKey, TElem>(this Dictionary<TKey, List<TElem>> dict, TKey key, TElem value)
{
List<TElem> list;
if (dict.ContainsKey(key))
list = dict[key];
else
dict[key] = list = new List<TElem>();
list.Add(value);
return dict;
}
}
It helps make your adds simpler and easier to read.

ReactiveUI SelectMany form of CreateDerivedCollection

Using ReactiveUI 6.5, I'm trying achieve something like a SelectMany LINQ expression with the CreateDerivedCollection feature of RxUI. The objects in my source collection (type 'A') have an IsSelected property, as well as another collection property where the items are of type 'B'. I want to end up with an IReactiveDerivedList<B> which is a flattened list of all of the B objects from the A's that are selected. Hopefully that makes sense.
To make it a little more concrete, let's use an example of an app for browsing log files. Say we have a LogFileViewModel type and our main screen's view model has a list of these. Each instance in the list represents a log file on the system, and we present a list of these that the user can select. It's a multi-select list so they can select more than one at a time.
The LogFileViewModel then has an IsSelected boolean property, which will be set to true/false as the user selects or deselects the corresponding item in the list. And it has a property which is a List<LogEntry>. Each LogEntry object of course represents one entry in the corresponding log file.
What I want to do then is have a reactive list in the main view model which is a list of all of the LogEntry objects for all of the currently selected LogFileViewModel objects. The ReactiveList of selected log files is easy, but I'm stuck on the second part. Here's what the main view model would basically look like:
public class MainViewModel
{
public MainViewModel()
{
//This gets initialized with the log files somehow, doesn't matter
LogFiles = new List<LogFileViewModel(...);
SelectedLogFiles = LogFiles.CreateDerivedCollection(l => l, l => l.IsSelected);
SelectedLogFileEntries = ? //How to create this one?
}
public List<LogFileViewModel> LogFiles { get; private set; }
public IReactiveDerivedList<LogFileViewModel> SelectedLogFiles { get; private set; }
public IReactiveDerivedList<LogEntry> SelectedLogFileEntries { get; private set; }
}
Is there a known way to do this that I'm just not seeing? If not, any clever ideas to achieve this behavior? :-)
Edit :
Looks like I missed this question in my initial search. Paul provided the "clever" solution to this problem about 2 years ago. So my question now ... is this still the best way to achieve this behavior?

How to store an IEnumerable and a string that describes it together

The IEnumerable in question is a list of an object containing filenames and properties relating to those file names, and the string is a directory name, such as "C:\Test\Testing". My goal is to store these two pieces of data together so that they're 'linked', in a sense, which should make them easier to use together, as the IEnumerable will become the source for a DataGrid, and the string the text of a label stating the current directory.
How would I go about achieving this? I initially thought of a dictionary, but that doesn't seem to work here. I need to be able to grab the 'top-most' item, so to speak, whenever a button is pressed, and dictionaries are, I believe, unordered.
While the accepted answer works, I would recommend creating a class for this, rather than using a KeyValuePair:
public class FilesInDirectory
{
public string Directory { get; set; }
public IEnumerable<string> FileNames { get; set; }
}
In OOP you should strive to avoid primitive obsession. A KeyValuePair is a kind of primitive, as it doesn't convey any meaning and can't be extended.
Meaning: When you're iterating over the list and dealing with item.Key and then iterating over item.Value, things will get confusing - far less so than item.Directory and item.FileNames
Extension: You will want to define some methods that extract data from these items, or process them in some way - usually, these methods are better off on the data item itself. For example you might want to get a list of actual file objects. With a KeyValuePair, you will have to define that method on some other class, whereas it actually fits right at home on FilesInDirectory.GetFiles()
How about a List<KeyValuePair<IEnumerable, string>>?
var list = new List<KeyValuePair<IEnumerable, string>>();
list.Add(new KeyValuePair<IEnumerable, string>(data, path));
If that sounds too awkward for you, feel free to use a custom class instead.
Feel free to use a Queue<>, Stack<> or anything else that fits your needs, KeyValuePair can be used with any collection type.

Should I create enum or collection for positions?

I would like to create sample program.
It will has Department Class,Position Class,Employee Class.
Each department will has any positions. I don't know that how I should specify that.
And I don't use DataBase. It is only to learn OOP in Console.
Whether to use an Enum or a Collection mainly depends on the following criteria:
How often you expect the available values of positions to change?
Who will conduct the changes?
Does the Position only have an Id and a Text or do you expect it to have other data fields as well.
Will each position be available once per Department and not several times?
If your answers are
Seldom.
The developer.
Only Id and Text.
Yes
then an Enum with a FlagsAttribute is a good option. However, if there is any doubt about this, creating a class for a Position and adding a collection is a better way as it is more flexible. It also allows you to add the same kind of position several times to a Department, if this is a requirement.
Though you don't use a database up to now, also note that adding an Enum property to the Department is much easier to save in comparison to have related positions that should go into a separate structure.
Based upon your comment, I suggest to add a constructor to set the relevant data in a department upon creation (you might also think about whether the data should be private fields as you could not access them from the outside):
class Department
{
public Department(Dep departmentType, IEnumerable<Position> positions, int employeeCount)
{
this.departmentType = departmentType;
this.positions = new List<Position>(positions);
this.employeeCount = employeeCount;
}
Dep departmentType;
List<Position> positions;
int employeecount;
}
You could then create a new Department like this:
var dep = new Department(Dep.Electric,
new Position() [] { new Position(), new Position() },
5);
Please note that you might also need to add a constructor to your Position class. In the above sample, I create two Position objects that are added to the Positions list in the Department.
If position is going to be just string values, its best to have them as enum and write your logic around it.
If new position gets added to the enum, the logic will break and it would help you to write specific functionality for each position.
You can write a class around this position enum to alter its functionality based on the enum.

Categories