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");
}
}
Related
I am populating an array with instances of a class:
BankAccount[] a;
. . .
a = new BankAccount[]
{
new BankAccount("George Smith", 500m),
new BankAccount("Sid Zimmerman", 300m)
};
Once I populate this array, I would like to sort it by balance amounts. In order to do that, I would like to be able to check whether each element is sortable using IComparable.
I need to do this using interfaces. So far I have the following code:
public interface IComparable
{
decimal CompareTo(BankAccount obj);
}
But I'm not sure if this is the right solution. Any advice?
You should not define IComparable yourself. It is already defined. Rather, you need to implement IComparable on your BankAccount class.
Where you defined the class BankAccount, make sure it implements the IComparable interface. Then write BankAccount.CompareTo to compare the balance amounts of the two objects.
public class BankAccount : IComparable<BankAccount>
{
[...]
public int CompareTo(BankAccount that)
{
if (this.Balance < that.Balance) return -1;
if (this.Balance == that.Balance) return 0;
return 1;
}
}
Edit to show Jeffrey L Whitledge's solution from comments:
public class BankAccount : IComparable<BankAccount>
{
[...]
public int CompareTo(BankAccount that)
{
return this.Balance.CompareTo(that.Balance);
}
}
IComparable already exists in .NET with this definition of CompareTo
int CompareTo(Object obj)
You are not supposed to create the interface -- you are supposed to implement it.
public class BankAccount : IComparable {
int CompareTo(Object obj) {
// return Less than zero if this object
// is less than the object specified by the CompareTo method.
// return Zero if this object is equal to the object
// specified by the CompareTo method.
// return Greater than zero if this object is greater than
// the object specified by the CompareTo method.
}
}
Do you want to destructively sort the array? That is, do you want to actually change the order of the items in the array? Or do you just want a list of the items in a particular order, without destroying the original order?
I would suggest that it is almost always better to do the latter. Consider using LINQ for a non-destructive ordering. (And consider using a more meaningful variable name than "a".)
BankAccount[] bankAccounts = { whatever };
var sortedByBalance = from bankAccount in bankAccounts
orderby bankAccount.Balance
select bankAccount;
Display(sortedByBalance);
An alternative is to use LINQ and skip implementing IComparable altogether:
BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();
There is already IComparable<T>, but you should ideally support both IComparable<T> and IComparable. Using the inbuilt Comparer<T>.Default is generally an easier option. Array.Sort, for example, will accept such a comparer.
If you only need to sort these BankAccounts, use LINQ like following
BankAccount[] a = new BankAccount[]
{
new BankAccount("George Smith", 500m),
new BankAccount("Sid Zimmerman", 300m)
};
a = a.OrderBy(bank => bank.Balance).ToArray();
If you need to compare multiple fields, you can get some help from the compiler by using the new tuple syntax:
public int CompareTo(BankAccount other) =>
(Name, Balance).CompareTo(
(other.Name, other.Balance));
This scales to any number of properties, and it will compare them one-by-one as you would expect, saving you from having to implement many if-statements.
Note that you can use this tuple syntax to implement other members as well, for example GetHashCode. Just construct the tuple and call GetHashCode on it.
This is an example to the multiple fields solution provided by #Daniel Lidström by using tuple:
public static void Main1()
{
BankAccount[] accounts = new BankAccount[]
{
new BankAccount()
{
Name = "Jack", Balance =150.08M
}, new BankAccount()
{
Name = "James",Balance =70.45M
}, new BankAccount()
{
Name = "Mary",Balance =200.01M
}, new BankAccount()
{
Name = "John",Balance =200.01M
}};
Array.Sort(accounts);
Array.ForEach(accounts, x => Console.WriteLine($"{x.Name} {x.Balance}"));
}
}
public class BankAccount : IComparable<BankAccount>
{
public string Name { get; set; }
public int Balance { get; set; }
public int CompareTo(BankAccount other) =>
(Balance,Name).CompareTo(
(other.Balance,other.Name ));
}
Try it
I get DataTable like this
ID Name Type XML
1 Test Pro <sample><Name>xyz</Name><Type>xyz</Type><Date>2015-01-01</Date></sample>
2 Test2 Pro2 <sample><Name>abc</Name><Type>pqr</Type><Date>2015-01-02</Date></sample>
I convert this to class as shown below
Public class test
{
public int ID{get;set;)
public int Name{get;set;)
public int Type{get;set;)
public dictionary<string,string> XML{get;set;)
}
This XML contains node and its values as key value pair. Now, i want to sort this based on the user input. e.g. if user wants to sort by Date or name of Type. How to do that? Sort in either datatable or directly into the list will work.
I tried to sort in datatable but all the time result remains the same. Please provide any suggestions for the same.
You can use an instance of a class that implements IComparer for your type. The comparer implementation then knows which rules to apply, for example when your type has a Dictionary.
Here is an example implementation to get you started. Do note that I didn't bother to implement all the different edge cases with null values. That is left as an exercise for the reader.
public class Test
{
public int ID{get;set;}
public int Name{get;set;}
public int Type{get;set;}
public Dictionary<string,string> XML{get;set;}
// this class handles comparing a type that has a dictionary of strings
private class Comparer: IComparer<Test>
{
string _key;
// key is the keyvalue from the dictionary we want to compare against
public Comparer(string key)
{
_key=key;
}
public int Compare(Test left, Test right)
{
// let's ignore the null cases,
if (left == null && right == null) return 0;
string leftValue;
string rightValue;
// if both Dictionaries have the key we want to sort on ...
if (left.XML.TryGetValue(_key, out leftValue) &&
right.XML.TryGetValue(_key, out rightValue))
{
// ... lets compare on those values
return leftValue.CompareTo(rightValue);
}
return 0;
}
}
// this method gives you an Instace that implements an IComparer
// that knows how to handle your type with its dictionary
public static IComparer<Test> SortOn(string key)
{
return new Comparer(key);
}
}
Use the above class in any Sort method that takes an IComparer, for example, on a plain List you can do:
list.Sort(Test.SortOn("Date"));
And that will sort the list.
To test the above code you can use this test rig:
var list = new List<Test> {
new Test {ID=1, Name =2, Type=3,
XML = new Dictionary<string,string>{{"Date","2017-09-01"}}},
new Test {ID=10, Name =20, Type=30,
XML = new Dictionary<string,string>{{"Date","2017-01-01"}}},
new Test {ID=100, Name =200, Type=300,
XML = new Dictionary<string,string>{{"Date","2017-03-01"}}},
};
list.Sort(Test.SortOn("Date"));
list.Dump();
I have a datarow filled with ints stored as strings.
I can parse each one like so: Convert.ToInt32(row[1].ToString())
It works, but is a bit unsightly. I got the idea that maybe I could use an extension class to try and make a .ToInt method. Simplifies things a bit. After some research, I wrote this up:
static class DataRowHelper
{
public static int ToInt(this DataRow row)
{
return Convert.ToInt32( row.ToString());
}
}
This almost gets me there, but the problem is that it attaches the new method to the row like this: row.ToInt() instead of of attaching it like this: row[1].ToInt()
What am I missing here?
I feel the other answers are offering solutions, but not answers.
The reason your extension method isn't working is because the return type of the indexer of your DataRow - row[1] isn't a DataRow, it's a string.
You should enhance your extension method to allow for this indexer:
public static class DataRowExtensions
{
public static int ToInt(this DataRow row, int index)
{
return Convert.ToInt32(row[index].ToString());
}
}
You don't have to apply your own extension method to achieve what you're describing in your question, because the MS team already wrote one for you (terrybozzio hinted at it in a comment).
Instead of using syntax like this:
var id = Convert.ToInt32(row[1]);
var id = Convert.ToInt32(row["id"]);
There's a strongly-typed Field extension method that allows you to specify the data type:
var id = row.Field<int>(1);
var id = row.Field<int>("id");
I can't imagine you'll get much shorter or clearer than that by writing your own extension method.
You could just add the column number as a parameter:
int i1 = row.ToInt(1);
int i2 = row.ToInt(2);
etc.
in which case the extension method would be (error handling not included):
public static int ToInt(this DataRow row, int column)
{
return Convert.ToInt32(row[column].ToString());
}
Even better if you want to handle generics and DbNull:
static class DataRowHelper
{
public static int ToInt(this object item)
{
return Convert.ToInt32(item == null ? 0 : item);
}
}
This is not very exception-safe, and will throw on quite a few conditions. You might want to add some checking here, or implement a TryToInt() type method.
Your extension method should inherit a string, not a DataRow. It should look like this (keep in mind this is very simple... and you should probably include error handling of some sort):
static class DataRowHelper
{
public static int ToInt(this string item)
{
return Convert.ToInt32(item);
}
}
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.
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);