Sorting C# List based on its element - c#

I have the C# class as follows :
public class ClassInfo {
public string ClassName;
public int BlocksCovered;
public int BlocksNotCovered;
public ClassInfo() {}
public ClassInfo(string ClassName, int BlocksCovered, int BlocksNotCovered)
{
this.ClassName = ClassName;
this.BlocksCovered = BlocksCovered;
this.BlocksNotCovered = BlocksNotCovered;
}
}
And I have C# List of ClassInfo() as follows
List<ClassInfo> ClassInfoList;
How can I sort ClassInfoList based on BlocksCovered?

myList.Sort((x,y) => x.BlocksCovered.CompareTo(y.BlocksCovered)

This returns a List<ClassInfo> ordered by BlocksCovered:
var results = ClassInfoList.OrderBy( x=>x.BlocksCovered).ToList();
Note that you should really make BlocksCovered a property, right now you have public fields.

If you have a reference to the List<T> object, use the Sort() method provided by List<T> as follows.
ClassInfoList.Sort((x, y) => x.BlocksCovered.CompareTo(y.BlocksCovered));
If you use the OrderBy() Linq extension method, your list will be treated as an enumerator, meaning it will be redundantly converted to a List<T>, sorted and then returned as enumerator which needs to be converted to a List<T> again.

I'd use Linq, for example:
ClassInfoList.OrderBy(c => c.ClassName);

Related

C# Class/Object passing (Array of Objects)

Right so i have a class I'm using to store a set of values
public class dataSet
{
public int Number;
public double Decimal;
public string Text;
//etc...
}
Then I've made an array of type dataSet
public static dataSet[] dataOne = new dataSet[100];
And i'm trying to sort the array of dataOne relevant to the values stored in the int Number stored within dataSet.
I have a sort algorithm ready but i'm struggling to pass in the values stored solely in dataOne.Number so it just ends up being an integer array that i'm passing to the sort.
I'm a total noob at programming so any help would be greatly appreciated.
Edit:
I need to call my sort function by passing it in the array of dataOne.Number if this is possible? So it's basically just passing the sort function an int[]
Give you already have data into your array named dataOne, you could try:
Linq Solution
Use linq to sort it, try this:
dataOne = dataOne.OrderBy(x => x.Number).ToArray();
Remember to add the namespace System.Linq to have access into these methods.
OrderBy allows you to pass an expression to sort data and it will return an IOrderedEnumerable. The ToArray will convert it to an array.
Not Linq Solution
If you are not allowed to use Linq. You could implement an class that implements IComparer<T> and implement the method Compare which takes two generics arguments. Use an instance of this comparer type to sort your data.
For sample, since you have your dataSet type defined, you could implement the comparer:
public class DataSetComparer : IComparer<dataSet>
{
public int Compare(dataSet x, dataSet y)
{
// define the logic to sort here...
return x.Number.CompareTo(y.Number);
}
}
And then, use the comparer on the Array.Sort method:
Array.Sort(dataSet, new NumberComparer());
It will order your dataSets.
I'm not sure I follow why you can't use Linq. But that forces you do to something like this:
var numberValues = new List<int>();
foreach(var dataItem in dataOne)
{
numberValues.Add(dataItem.Number);
}
Then you could pass numberValues.ToArray() to your sort method.
With Linq it would just be
dataOne.Select(d => d.Number).ToArray()
You should have dataset implement IComparable that way you can easily just do...
dataOne = dataOne.OrderBy(x => x).ToArray();
OR...
Array.Sort(dataOne);
Here is how to implement IComparable...
public class dataSet : IComparable
{
public int Number;
public double Decimal;
public string Text;
public int CompareTo(object obj)
{
if (obj == null)
return 1;
dataSet other = obj as dataSet;
if (other != null)
return this.Number.CompareTo(other.Number);
else
throw new ArgumentException("Object is not a dataSet");
}
}

Comparing for sorting in different ways

I have a class that implements IComparable. It works but the comparison is static, i.e. it's always the same ordering it achieves. What would be a good method to introduce comparison by a parameter, i.e. if we have:
class Poo : IComparable {
public int A { ... };
public int B { ... };
...
}
IEnumerable<Foo> list = ...;
list = list.Sort(???);
I' d like to order the list with respect to A or B depending on the parameter passed to Sort at the question marks. What's the most efficient way to do that?
At the moment, the best method I've came up with is to declare a couple of methods that I pass to Sort as delegates.
private static int CompareWrtA(Foo foo1, Foo foo2) { ... }
private static int CompareWrtB(Foo foo1, Foo foo2) { ... }
if(withRespectToA)
list = list.Sort(CompareWrtA);
else
list = list.Sort(CompareWrtB);
But it doesn't feel really as the best way. Criticism is welcome.
If you want to simplify that statement you can write it like this:
list.Sort((x,y) => withRespectToA ? CompareWrtA(x,y) : CompareWrtB(x,y));
BTW, Sort method is modifying your list, it doesn't return anything.So you don't need to assign it back to your list.

way to go from list of objects to SortedList<string,string> with two of the fields using LINQ

I have class with 5 fields.
public class Test
{
public string name;
public string description;
public int int1;
public int int2;
public int int3;
}
In one of my function I have List<Test> list which has 10 items. Here I want SortedList<string,string> for two properties name & description.
I know, I can achieve this using for each but I want to know How can I do this using LINQ?
Use this:
var result = list.OrderBy(x => x.Name).ThenBy(x => x.Description);
Important:
Don't use multiple calls to OrderBy as they overwrite each other.
The sorted result will be in result. The original list remains unchanged.
The answer from #HugoRune is quite exhaustive, but because you said you want to use Linq, I'd suggest to add an extension method in your scope to help you with your goal:
static class SortedListExtensions
{
public static SortedList<K, V> ToSortedList<K, V, T>(
this IEnumerable<T> source,
Func<T, K> keySelector, Func<T, V> valueSelector)
{
return new SortedList<K,V>(
source.ToDictionary(
cur => keySelector(cur),
cur => valueSelector(cur)));
}
}
this way your SortedList creation is composable in Linq computations:
var sl = list.ToSortedList(f => f.name, f => f.description);
A C# SortedList is a type of dictionary, not actually a list.
If you indeed want a SortedList containing the names as keys and the descriptions as values, you can use this:
SortedList slist = new SortedList(list.ToDictionary(t=>t.name, t=>t.description))
Be aware that if a name occurs twice this will throw an exception, since dictionary keys have to be unique.
For most practical purposes however, the solution posted by Daniel Hilgarth is what I would use, unless you have a library function that specifically requires a SortedList as parameter.

Selecting DataRows into new structures using LINQ. Calling Distinct() fails

Consider these two structures:
struct Task
{
public Int32 Id;
public String Name;
public List<Registration> Registrations;
}
struct Registration
{
public Int32 Id;
public Int32 TaskId;
public String Comment;
public Double Hours;
}
I am selecting a bunch of entries in a DataTable into new structures, like so:
var tasks = data.AsEnumerable().Select(t => new Task
{
Id = Convert.ToInt32(t["ProjectTaskId"]),
Name = Convert.ToString(t["ProjectTaskName"]),
Registrations = new List<Registration>()
});
But when I call Distinct() on the collection, it doesn't recognize objects with the same values (Id, Name, Registrations) as being equal.
But if I use an equality comparer; comparing the Id property on the objects, it's all fine and dandy...:
class TaskIdComparer : IEqualityComparer<Task>
{
public bool Equals(Task x, Task y)
{
return x.Id == y.Id;
}
public Int32 GetHashCode(Task t)
{
return t.Id.GetHashCode();
}
}
What am I missing here? Is Distinct() checking something else than the value of properties?
LINQ's Distinct method compares objects using the objects' Equals and GetHashCode implementations.
Therefore, if these methods are not overridden, it will compare by reference, not by value.
You need to use an EqualityComparer. (Or implement Equals and GetHashCode for the Task class)
my guess is that it's the list in there. Almost certainly, the two list objects are different, even if they contain the same info.

Interfaces and casting lists

Why doesnt this work, and how to fix?
public interface ITheInterface
{
string a{get;set;}
string b{get;set;}
}
public class SomeObject: ITheInterface
{
string a{get;set;}
string b{get;set;}
...
}
public class SomeGroup
{
ITheInterface Result;
...
}
var results= from y in dc.Groups
where y.id==1
select new SomeGroup
{
Result= (from x in dc.Objects
select new SomeObject{... }
).SingleOrDefault(),
}
return results.ToList();
Could not convert from type System.Collections.Generic.List to Interface
I assume your problem is with the Results.ToList() call? It will fail, because ITheInterface does not support ToList(). You are calling SingleOrDefault() on the LINQ query, which is giving you a single item. It doesn't make sense to call ToList() on a single item.
If, instead, your code read like this:
IEnumerable<SomeObject> Results = from x in dc.Objects
select new SomeObject{... };
Then, Results.ToList() will give you a List<SomeObject>.
If what you are actually looking for is a List<ITheInterface> instead, you can do this:
Results.Cast<ITheInterface>().ToList()
Results is a single object; ToList() only works on Enumerables.
You need to either write return new List { Results }; (this uses a collection initializer) or get rid of the call to SingleOrDefault and declare Results as an IEnumerable<ITheInterface>.
If you only want one object, why are you returning a List?
In addition to the other answers, when you implement an interface, you must declare the member functions as public:
public class SomeObject: ITheInterface
{
public string a{get;set;}
public string b{get;set;}
...
}
You want to say
SomeGroup result = results.SingleOrDefault()
return result;
This is because the return type of your method is SomeGroup (I'm inferring)

Categories

Resources