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.
Related
Imagine I have several classes of different types with the same property.
I want to make a generic method that should acess the value of the property of those classes.
The following code is invalid and will throw this error :
Cannot resolve method 'GetValue(T)' (...)
.. so what's the proper way to do this?
public static List<int> FooBar<T>(T myObject)
{
var myValue = typeof(T).GetProperty("myList").GetValue(myObject);
return myValue;
}
The "proper" way to do this is probably to have a where T : SomeInterfaceOrBaseType, i.e. the thing that has a myList member - and then just access it; i.e. return myObject.myList;
interface IFoo {
List<int> myList {get;}
}
public static List<int> FooBar<T>(T myObject) where T : IFoo {
return myObject.myList;
}
But... by the time you've done that, there's not really a need for the method any more, as if the caller knows that the type is an IFoo, they can do that themselves.
If you must do it via reflection... well, that's hard. It might be easier to just abuse dynamic:
public static List<int> FooBar<T>(T myObject)
{
dynamic obj = myObject;
List<int> myList = obj.myList;
return myList;
}
you can do like this myObject.GetType().GetProperty("myList").GetValue(myObject, null);
If you really want to do this with generics, try the code below
return (List<int>)(typeof(T).GetProperty("myList").GetValue(myObject, null))
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");
}
}
Right now my code looks like this:
var ids = projectId.HasValue ? new List<Guid> { projectId.Value } : new List<Guid>();
Is there a more succinct way of creating a list in one line of code, with one element added optionally?
Another idea for an extension method (the name could definitely be improved, maybe PossiblyCreateSingletonList?):
public static class NullableExtensions
{
public static List<T> SingletonList<T>(this Nullable<T> item) where T : struct
{
return item.HasValue ? new List<T> { item.Value } : new List<T>();
}
}
Usage:
Guid? projectId = null;
List<Guid> projectIds = projectId.SingletonList(); // empty list
I would solve this using a extension method like this:
public static void AddIfNotNull<T>(this List<T> list, T? value) where T : struct
{
if(value != null)
{
list.Add(value.Value);
}
}
Than it could be used like this:
var ids = new List<Guid>();
ids.AddIfNotNull(projectId);
Maybe not as "crafty" (and not a one-liner) as your proposal, but in my opinion it is much easier to read and understand. If desired to be used as a one-liner you could modify the return type of the extension to be the list. That would make it possible to be used something like var ids = new List<Guid>().AddIfNotNull(projectId);
This probably isn't a good idea, but in C# 6, collection initializers also work when Add() is an extension method.
This means you can write the extension Add() like this:
public static void Add<T>(this List<T> list, T? item) where T : struct
{
if (item.HasValue)
{
list.Add(item.Value);
}
}
And then this code will do what you want:
var list = new List<Guid> { projectId };
Note that this will only work for value types (because of the T/T? distinction) and there is no simple way to make it work for reference types.
Also, I would find the line above very surprising, being more succinct is not always better. Which is why I actually wouldn't use this code.
That's pretty succinct, but another option would be to use LINQ:
var ids = new[] { projectId }.Where(x => x.HasValue).Select(x => x.Value).ToList();
If you're going the extension method route, it would have to look something like:
public static void AddIfNotNull<T>(this List<T> list, T? value)
where T : struct
{
if (value.HasValue)
{
list.Add(value.Value);
}
}
You'd have to build a second extension method for reference types (where T : class) if you needed.
Let's say we have a class
class ComplexCls
{
public int Fld1;
public string Fld2;
//could be more fields
}
class Cls
{
public int SomeField;
}
and then some code
class ComplexClsList: List<ComplexCls>;
ComplexClsList myComplexList;
// fill myComplexList
// same for Cls
class ClsList : List<Cls>;
ClsList myClsList;
We want to populate myClsList from myComplexList, something like (pseudocode):
foreach Complexitem in myComplexList
{
Cls ClsItem = new Cls();
ClsItem.SomeField = ComplexItem.Fld1;
}
The code to do this is easy and will be put in some method in myClsList.
However I'd like to design this as generic as possible, for generic ComplexCls.
Note that the exact ComplexCls is known at the moment of using this code, only the algorithm shd be generic.
I know it can be done using (direct) reflection but is there other solution?
Let me know if the question is not clear enough. (probably isn't).
[EDIT] Basically, what I need is this: having myClsList, I need to specify a DataSource (ComplexClsList) and a field from that DataSource (Fld1) that will be used to populate my SomeField
This is just a mapping, so use some simple LINQ:
ClsList myClsList = new ClsList();
myClsList.AddRange(
myComplexList.Select(Complexitem => new Cls { SomeField = Complexitem.Fld1 })
);
Okay, the easier version assuming we have a known target field on a class (I've written this as an extension method, no need to do
public IEnumerable<Cls> MapField<TSource>(IEnumerable<TSource> sourceList,
Func<TSource, int> sourceSelector)
{
return sourceList.Select(x => new Cls {SomeField = sourceSelector(x)});
}
Called this way
IEnumerable<Cls> result = MapField(myComplexList, x => x.Fld1);
Aside: Since your myComplexList of type ComplexClsList inherits from List (which implements IEnumerable this will work. The result isn't of type ClsList that you wanted, but you could easily call .ToList() on the result and provide a constructor on ClsList that takes a List<Cls>.
And the more complicated version for when we don't know the target field (or type)...
public IEnumerable<TResult> MapField<TSource, TResult, TMap>(
IEnumerable<TSource> sourceList,
Func<TSource, TMap> sourceSelector,
Func<TMap, TResult> resultCreator)
{
return sourceList.Select(x => resultCreator(sourceSelector(x)));
}
Not as pretty to call....
IEnumerable<Cls> result = MapField(
myComplexList,
source => source.Fld1,
valueToMap => new Cls() {SomeField = valueToMap});
Might be a better way, but it's not occurring to me at the moment.
Edit: Actually, you could combine the two Func on the last one into a single one that takes a TSource and creates and maps the necessary fields to TResult, but I'm really not sure what you're gaining with that extra layer of abstraction...
You may want to reconsider extending List classes in the first place. What does inheritance give you, in this case? I suspect that you'll be better off favoring composition over inheritance here. One possible approach would be:
// If you would say that a ComplexCls "is a" Cls, then maybe your inheritance
// relationship belongs here instead.
public class ComplexCls : Cls {
}
public class ClsList
{
public IReadOnlyCollection<Cls> Items {get;set;}
}
public class ComplexClsList
{
public IReadOnlyCollection<ComplexCls> Items {get;set;}
}
Then you can create a ClsClist easily.
ClsList basicList = new ClsList{Items = complexList.Items};
But you may want to take it a step farther and question why the ClsList and ComplexClsList classes exist at all. Why not simply pass around Lists directly. I mean, what's the difference between a ClsList and a "List of Clses" (List<Cls>)?
I have a List of a "complex" type - an object with a few string properties. The List itself is a property of another object and contains objects of a variety of types, as shown in this abbreviated class structure:
Customer {
public List<Characteristic> Characteristics;
.
.
.
}
Characteristic {
public string CharacteristicType;
public string CharacteristicValue;
}
I'd like to be able to collect a List of the values of a given type of Characteristics for the current Customer, which I can do in a 2-step process as follows:
List<Characteristic> interestCharacteristics = customer.Characteristics.FindAll(
delegate (Characteristic interest) {
return interest.CharacteristicType == "Interest";
}
);
List<string> interests = interestCharacteristics.ConvertAll<string>(
delegate (Characteristic interest) {
return interest.CharacteristicValue;
}
);
That works fine, but it seems like a long way around. I'm sure I must be missing a simpler way of getting to this list, either by chaining together the FindAll() and Convert() methods, or something else I'm overlooking entirely.
For background, I'm working in .Net 2.0, so I'm limited to the .Net 2 generics, and the Characteristic class is an external dependency - I can't change it's structure to simplify it, and there are other aspects of the class that are important, just not in relations to this problem.
Any pointers or additional reading welcomed.
Here's a generator implementation
public static IEnumerable<string> GetInterests(Customer customer)
{
foreach (Characteristic c in customer.Characteristics)
{
if (c.CharacteristicType == "Interest")
yield return c.CharacteristicValue;
}
}
sadly 3.5 extension methods and lambda are out based on your requirements but for reference here's how to do it:
customer.Characteristics
.Where(c => c.CharacteristicType == "Interest")
.Select(c => c. CharacteristicValue);
I would do some of the work manualy. By doing a FindAll first, and then a Convert, you're looping through your collection twice. It doesn't seem neccessary. If all you want at the end of the day, is a List of CharacteristicValue then just loop through your original collection, and add the CharacteristicValue to a List of each one that matches your criteria. Something like this:
Predicate<Characteristic> criteria = delegate (Characteristic interest)
{
return interest.CharacteristicType == "Interest";
};
List<string> myList = new List<string>();
foreach(Characteristic c in customer.Characteristics)
{
if(criteria(c))
{
myList.Add(c.CharacteristicValue);
}
}
Why not create a Dictionary<string, List<string>>, that way you can add "Interest" as the key, and a list of values as the value. For example:
Customer {
public Dictionary<string, List<string>> Characteristics;
.
.
.
}
...
Characteristics.Add("Interest", new List<string>());
Characteristics["Interest"].Add("Post questions on StackOverflow");
Characteristics["Interest"].Add("Answer questions on StackOverflow");
..
List<Characteristic> interestCharacteristics = Characteristics["Interest"];
Furthermore, if you wanted, you could limit your characteristics to a list of possible values by making it an enum, then use that as the data type of your dictionary's key:
public enum CharacteristicType
{
Interest,
Job,
ThingsYouHate
//...etc
}
then declare your dictionary as:
public Dictionary<CharacteristicType, List<string>> Characteristics;
..
Characteristics.Add(CharacteristicType.Interest, new List<string>());
Characteristics[CharacteristicType.Interest].Add("Post questions on StackOverflow");
Characteristics[CharacteristicType.Interest].Add("Answer questions on StackOverflow");