I have written a generic sort funciton to sort list and dicitonary. But LINQ doesnt works on Unity due to JIT errors. I want to have the same generics and convert it into myList.Sort() which uses CompraeTo. But Im unable to figure out how to accomplish this as generic as this.
public static List<T> MySort<T>(this List<T> source, Type typeOfObject, bool isAscending = false, params string[] param)
{
if(param.Length == 0)
return source;
if (isAscending)
{
var temp = source.OrderBy (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));
for (int i=1; i<param.Length; i++)
{
var myVar = i;
temp = temp.ThenBy((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
}
return temp.ToList();
}
else
{
var temp = source.OrderByDescending (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));
for (int i=1; i<param.Length; i++)
{
var myVar = i;
temp.ThenByDescending((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
}
return temp.ToList();
}
}
USage of this function
RealEstateItems.MySort(typeof(mIsoObjectExt), true, "UnlockLevel", "Coins", "Diamonds");
My current CompareTo Approac
myList.Sort ((a,b) => {
int result = ((a.Value) as mIsoObjectExt).UnlockLevel.CompareTo(((b.Value) as mIsoObjectExt).UnlockLevel);
// result == 0 ? result = a.Value.Coins.CompareTo(a.Value.Coins);
if(result == 0)
{
result = ((a.Value) as mIsoObjectExt).Coins.CompareTo(((b.Value) as mIsoObjectExt).Coins);
}
else
{
return result;
}
if(result == 0)
{
return ((a.Value) as mIsoObjectExt).Diamonds.CompareTo(((b.Value) as mIsoObjectExt).Diamonds);
}
return result;
});
But Im not satisfied with this i have to do this every time i have to sort even on the same properties. Basically i want to make something like above that i tell the function the type its properties to sort on and it sorts. How can i do this with Compare/CompareTo?
So we're going to need a few different building blocks to begin with. First off, what you're really doing here is sorting each item on a collection of values, as is seen in this other question. We can pull the solution from there to have a comparer for sorting items based on a collection of values:
public class SequenceComparer<T> : IComparer<IEnumerable<T>>
{
private IComparer<T> comparer;
public SequenceComparer(IComparer<T> compareer = null)
{
this.comparer = comparer ?? Comparer<T>.Default;
}
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
{
using (var first = x.GetEnumerator())
using (var second = x.GetEnumerator())
{
while (true)
{
var firstHasMore = first.MoveNext();
var secondHasMore = second.MoveNext();
if (!firstHasMore && !secondHasMore)
return 0;
var lengthComparison = firstHasMore.CompareTo(secondHasMore);
if (lengthComparison != 0)
return lengthComparison;
var nextComparison = comparer.Compare(first.Current, second.Current);
if (nextComparison != 0)
return nextComparison;
}
}
}
}
We also want a way of creating a Comparison<T> delegate (which List.Sort accepts) from a projection delegate. This method is simple enough to write:
public static Comparison<T> CreateComparison<T, TKey>(Func<T, TKey> selector,
IComparer<TKey> comparer = null)
{
comparer = comparer ?? Comparer<TKey>.Default;
return (a, b) => comparer.Compare(selector(a), selector(b));
}
It'll also be useful for us to be able to reverse a Comparison<T> (to handle descending ordering):
public static Comparison<T> Reverse<T>(this Comparison<T> comparison)
{
return (a, b) => comparison(b, a);
}
Now to pull all of the pieces together. We can create a comparison that, for the projection, projects each item into a sequence of values that represent fetching each of the property names from the item using reflection. We can then reverse the comparer if we need a descending sort.
public static void MySort<T>(this List<T> source,
bool isAscending = false,
params string[] properties)
{
var type = typeof(T);
var comparison = CreateComparison((T item) =>
properties.Select(prop => type.GetProperty(prop).GetValue(item)),
new SequenceComparer<object>());
if (!isAscending)
comparison = comparison.Reverse();
source.Sort(comparison);
}
Note that if you can also use the sequence comparer to simplify the LINQ approach:
public static IEnumerable<T> MyOrdering<T>(this IEnumerable<T> source,
bool isAscending = false,
params string[] properties)
{
var type = typeof(T);
Func<T, IEnumerable<object>> selector = item =>
properties.Select(prop => type.GetProperty(prop).GetValue(item))
.ToList();
if (isAscending)
return source.OrderBy(selector, new SequenceComparer<object>());
else
return source.OrderByDescending(selector, new SequenceComparer<object>());
}
You can use Servy's approach with reflection. If you decide against reflection, you can use the below approach, but it still needs the comparison to be provided from the caller.
public class MultiValueComparer<T> : IComparer<T>
{
private IEnumerable<Comparison<T>> _comparisons;
public MultiValueComparer(IEnumerable<Comparison<T>> comparisons)
{
_comparisons = comparisons;
}
public int Compare(T x, T y)
{
foreach (var comparison in _comparisons)
{
var result = comparison(x, y);
if (result != 0)
return result;
}
return 0;
}
}
An extension method which takes a variable number of parameters
public static void Sort<T>(List<T> source, params Comparison<T>[] comparisons)
{
if (comparisons.Count() == 0)
return;
source.Sort(new MultiValueComparer<T>(comparisons));
}
Usage:
Ascending Order:
Sort(samples, (x, y) => x.Name.CompareTo(y.Name), (x, y) => x.Test.CompareTo(y.Test));
Descending Order:
Sort(samples, (x, y) => y.Name.CompareTo(x.Name), (x, y) => y.Test.CompareTo(x.Test));
Related
Doing Add for two elements like T a, T b is simple, Mark has provided a good solution using Expression tree here, which translates into following and is simple to use:
static T Add<T>(T a, T b)
{
// Declare Parameter Expressions
ParameterExpression paramA = Expression.Parameter(typeof(T), "valueA"),
paramB = Expression.Parameter(typeof(T), "valueB");
// add the parameters together
BinaryExpression body = Expression.Add(paramA, paramB);
// Compile it
Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
// Call it
return add(a, b);
}
Challenge that I have is there's a collection of List<T>, where all elements have to added as shown above. I have tried following, on same lines as above, but it doesn't work:
static T AddAll<T>(List<T> list)
{
var parameterExpressionList = list.Select((x,i) => (Expression)Expression.Parameter(typeof(T), "value"+i));
var body = parameterExpressionList
.Skip(1)
.Aggregate(parameterExpressionList.First(),
(paramA, paramB) => Expression.Add(paramA, paramB));
// Compile it
Func<List<T>, T> addAll = Expression.Lambda<Func<List<T>, T>>(body, parameterExpressionList.Cast<ParameterExpression>()).Compile();
return addAll(list);
}
Run-time error that I get is: Incorrect number of parameters supplied for lambda declaration. Any pointer, how to achieve, please note, I don't need a solution where I cumulatively pick two elements from actual list and call Add<T>(T a, T b) , since that would lead to multiple times compilation of the Expression tree, which is not efficient, as I would have > 100 K data points, any suggestion to make my code work would be great, I am not sure where it is going wrong.
Since you have created a generic function, just use it on the list (I added an optional Adder method to handle non-standard classes):
static T AddAll<T>(IEnumerable<T> src, Func<T, T, T> adder = null) {
// Declare Parameter Expressions
ParameterExpression paramA = Expression.Parameter(typeof(T), "valueA"),
paramB = Expression.Parameter(typeof(T), "valueB");
// add the parameters together
BinaryExpression body;
if (adder == null)
body = Expression.Add(paramA, paramB);
else
body = Expression.Add(paramA, paramB, adder.GetMethodInfo());
// Compile it
Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
// Call it
return src.Aggregate(default(T), (ans, n) => add(ans, n));
}
You can use the Adder parameter to handle things like strings:
var ans = AddAll(new[] { "a", "b", "c" }, String.Concat);
Since we know the type of T at compile time though, we can just call Sum:
static T AddAll2<T>(IEnumerable<T> src) {
var paramA = Expression.Parameter(typeof(IEnumerable<T>), "valueA");
var method = typeof(Enumerable).GetMethod("Sum", new[] { typeof(IEnumerable<T>) });
if (method != null) {
// Create lambda body
var body = Expression.Call(method, paramA);
// Compile it
Func<IEnumerable<T>, T> sum = Expression.Lambda<Func<IEnumerable<T>, T>>(body, paramA).Compile();
// Call it
return sum(src);
}
else
return default(T);
}
Of course, if you are going to call Sum, you don't need a lambda:
static T AddAll3<T>(IEnumerable<T> src) {
var method = typeof(Enumerable).GetMethod("Sum", new[] { typeof(IEnumerable<T>) });
if (method != null) {
// Call it
return (T)method.Invoke(null, new[] { src });
}
else
return default(T);
}
Just try to get every item from your list and then accumulate they into result.
static T AddAll<T>(List<T> list)
{
if (list.Count == 0)
{
// It's additional small case
return default(T);
}
var listParam = Expression.Parameter(typeof(List<T>));
var propInfo = typeof(List<T>).GetProperty("Item");
var indexes = list.Select((x, i) => Expression.MakeIndex(listParam, propInfo, new[] { Expression.Constant(i) }));
Expression sum = indexes.First();
foreach (var item in indexes.Skip(1))
{
sum = Expression.Add(sum, item);
}
var lambda = Expression.Lambda<Func<List<T>, T>>(sum, listParam).Compile();
return lambda(list);
}
You can pass list directly as argument and just create sum via indexes:
static T AddAll<T>(List<T> list)
{
if (list.Count == 0) return default(T);
if (list.Count == 1) return list[0];
var indexerProperty = typeof(List<T>).GetProperty("Item");
var p = Expression.Parameter(typeof(List<T>));
var exp = Expression.Add(
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(0) }),
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(1) }));
for (var i = 2; i < list.Count; i++)
{
exp = Expression.Add(
exp,
Expression.MakeIndex(p, indexerProperty, new [] { Expression.Constant(i) }));
}
var lambda = Expression.Lambda<Func<List<T>, T>>(exp, p).Compile();
return lambda(list);
}
Store all applicable Enumerable.Sum overloads in a dictionary:
// all methods with signature public static T Enumerable.Sum(IEnumerable<T>) by element type
private static readonly Dictionary<Type, MethodInfo> _sumMethodsByElementType = typeof(Enumerable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "Sum" && !m.IsGenericMethod)
.Select(m => new { Method = m, Parameters = m.GetParameters() })
.Where(mp => mp.Parameters.Length == 1)
.Select(mp => new { mp.Method, mp.Parameters[0].ParameterType })
.Where(mp => mp.ParameterType.IsGenericType && mp.ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(mp => new { mp.Method, ElementType = mp.ParameterType.GetGenericArguments()[0] })
.Where(me => me.Method.ReturnType == me.ElementType)
.ToDictionary(mp => mp.ElementType, mp => mp.Method);
Invoke the corresponding one from inside the generic AddAll (or Sum as I prefer to call it) method:
public static T Sum<T>(IEnumerable<T> summands)
{
MethodInfo sumMethod;
if (!_sumMethodsByElementType.TryGetValue(typeof(T), out sumMethod)) throw new InvalidOperationException($"Cannot sum elements of type {typeof(T)}.");
return (T)sumMethod.Invoke(null, new object[] { summands });
}
Test:
Console.WriteLine(Sum(new[] { 1, 2, 3 }));
Console.WriteLine(Sum(new[] { 1, 2, 3, default(int?) }));
Console.WriteLine(Sum(new[] { 1.1, 2.2, 3.3 }));
Console.WriteLine(Sum(new[] { 1.1, 2.2, 3.3, default(double?) }));
try { Console.WriteLine(Sum(new[] { 'a', 'b', 'c' })); }
catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); }
Output:
6
6
6.6
6.6
Cannot sum elements of type System.Char.
You don't necessarily need to solve every part of the problem with expressions, if you are just interested in the operation itself
Here's an implementation that uses Singleton via the Lazy<> type for the default addition of type T (instead of static methods)
The LinqExpression expression could potentially be reused if you really need expressions (say, in an EF scenario), but there's no equivalent expression for the AddAll operation... though it can potentially be expanded to support a generic expression for AddAll
public abstract class Addition<T>
{
private readonly Lazy<Expression<Func<T, T, T>>> _lazyExpression;
private readonly Lazy<Func<T, T, T>> _lazyFunc;
public Func<T, T, T> Execute
{
get { return _lazyFunc.Value; }
}
public Expression<Func<T, T, T>> LinqExpression
{
get { return _lazyExpression.Value; }
}
protected Addition()
{
_lazyExpression = new Lazy<Expression<Func<T, T, T>>>(InitializeExpression);
_lazyFunc = new Lazy<Func<T, T, T>>(() => LinqExpression.Compile());
}
protected abstract Expression<Func<T, T, T>> InitializeExpression();
}
public sealed class DefaultAddition<T> : Addition<T>
{
private static readonly Lazy<DefaultAddition<T>> _lazyInstance = new Lazy<DefaultAddition<T>>(() => new DefaultAddition<T>());
public static DefaultAddition<T> Instance
{
get {return _lazyInstance.Value; }
}
// Private constructor, you only get an instance via the Instance static property
private DefaultAddition()
{
}
protected override Expression<Func<T, T, T>> InitializeExpression()
{
var paramX = Expression.Parameter(typeof(T), "x");
var paramY = Expression.Parameter(typeof(T), "y");
var body = Expression.Add(paramX, paramY);
return Expression.Lambda<Func<T, T, T>>(body, paramX, paramY);
}
}
public static class Operations
{
public static T Add<T>(T x, T y)
{
return DefaultAddition<T>.Instance.Execute(x, y);
}
public static T AddAll<T>(IEnumerable<T> enumerable)
{
var itemAdd = DefaultAddition<T>.Instance.Execute;
return enumerable.Aggregate(default(T), (result, item) => itemAdd(result, item));
// This might be more efficient than Aggregate, but I didn't benchmark it
/*
var result = default(T);
foreach (var item in enumerable)
{
result = itemAdd(result, item);
}
return result;
*/
}
}
Usage:
// Can mix double with int :)
var doubleAdd = Operations.Add(4.5, 3);
// Can mix decimal with int :)
var listAdd = Operations.AddAll(new[] {3, 6.7m, 0.3m});
// Even empty enumerables
var shortAdd = Operations.AddAll(Enumerable.Empty<short>());
// This will not work for byte. System.Byte should be casted to System.Int32
// Throws "InvalidOperationException: The binary operator Add is not defined for the types 'System.Byte' and 'System.Byte'."
var byteAdd = Operations.AddAll(new byte[] {1, 2, 3});
If your T is of value type like int, long, double etc. then you can simply do this:
//add
//using System.Linq;
var items = new List<int>();
items.Add(1);
items.Add(5);
items.Add(10);
var sum = items.Sum();
I have the following method which determines which cars I need to delete from the DB.
private List<CarDTO> BuildCarsToDelete(IList<CarDTO> newCars, IList<CarDTO> existingCars)
{
var missingCars = new List<CarDTO>();
var cars = newCars.Select(c => c.CarId);
var newCarIds = new HashSet<int>(cars);
foreach (var car in existingCars)
{
//If there are no new cars then it had some and they have been removed
if (newCars.Count() == 0)
{
missingCars.Add(car);
}
else
{
if (!newCarIds.Contains(car.CarId))
{
missingCars.Add(car);
}
}
}
return missingCars;
}
This works as I want - but if I want to achieve the same functionality for Customers or Apartments of other DTOs I will be copying a pasting the code but only changing the variable names and the Type of DTO around - is there a nicer way possible using generics which would keep the algorithm and logic as it is but allow me to use on any DTO?
If all the ids are of type int then you can do that by passing in a Func to determine the id.
private List<T> BuildToDelete<T>(
IList<T> newItems,
IList<T> existingItems,
Func<T, int> getId)
{
var missingItems = new List<T>();
var items = newItems.Select(getId);
var newItemIds = new HashSet<int>(items);
foreach (var item in existingItems)
{
if (newItems.Count() == 0)
{
missingItems.Add(item);
}
else
{
if (!newItemIds.Contains(getId(item)))
{
missingItems.Add(item);
}
}
}
return missingItems;
}
Then call as shown below:
var results = BuildToDelete(newCars, existingCars, c => c.CarId);
Assuming you use the interface approach mentioned in comments, a generic version could look something like this:
private List<TEntity> BuildEntitiesToDelete(IList<TEntity> newEntities, IList<TEntity> existingEntities) where TEntity : IEntityWithId
{
var missingEntities = new List<TEntity>();
var entities = newEntities.Select(e => e.Id);
var newEntityIds = new HashSet<int>(entities);
foreach (var entity in existingEntities)
{
if (entities.Count() == 0)
{
missingEntities.Add(entity);
}
else
{
if (!newEntityIds.Contains(entity.Id))
{
missingEntities.Add(entity);
}
}
}
return missingEntities;
}
IEntityWithId is probably a poor name for the interface, but I'll leave picking a better name up to you.
Try something cleaner:
1) create flexible equality comparer (need to add some null checking etc.)
public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> comparer;
Func<T, int> hash;
public FuncEqualityComparer (Func<T, T, bool> comparer, Func<T, int> hash)
{
this.comparer = comparer;
this.hash = hash;
}
public bool Equals (T x, T y) => comparer (x, y);
public int GetHashCode (T obj) => hash (obj);
}
2) and now, just simply:
var carComparerByID = new FuncEqualityComparer<CarDTO> ((a, b) => a.CarId == b.CarId, x => x.CarId.GetHashCode ());
var result = existingCars.Except (newCars, carComparerByID).ToList ();
I have the following method that compares 2 list (of the same type) and returns the differences. How do I make this method accept lists of any type?
var differences = list1.Where(x => list2.All(x1 => x1.Name != x.Name))
.Union(list2.Where(x => list1.All(x1 => x1.Name != x.Name)));
To get the difference between two sets (with order independency and multiplicity independency), you can use: HashSet<T>.SymmetricExceptWith(IEnumerable<T>).
public static IEnumerable<T> GetSymmetricDifference<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer = null)
{
HashSet<T> result = new HashSet<T>(list1, comparer);
result.SymmetricExceptWith(list2);
return result;
}
In your case, to use it:
var difference = GetSymmetricDifference(list1, list2, new MyComparer());
With a custom comparer:
public class MyComparer : IEqualityComparer<MyType>
{
public bool Equals(MyType x, MyType y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(MyType obj)
{
return obj.Name == null ? 0 : obj.Name.GetHashCode();
}
}
What about this:
var differences = list1.Except(list2).Union(list2.Except(list1));
Im working with 2 List, i want to see if the main contains the same types. The 2 lists do not need to contain the same count or order, just have all of the matching Types. I know this is very possible with Linq, however i cannot use that.
private static bool ContentsMatch(List<Type> list1, List<Type> list2)
{
if (list1.Count != list2.Count)
return false;
for (int i = 0; i < list1.Count; i++)
{
if (!list1[i].Equals(list2[i]))
return false;
}
return true;
}
The above method i tried will only return true if they are in the same order.
Code for algorithm provided in the comments.
Does not depend on order or count or duplicate items. Also generic and abstracted.
bool IsSameSet<T>(IEnumerable<T> l1, IEnumerable<T> l2)
{
return IsSubSet(l1, l2) && IsSubSet(l2, l1);
}
bool IsSubSet<T>(IEnumerable<T> l1, IEnumerable<T> l2)
{
var lookup = new Dictionary<T, bool>();
foreach (var e in l1)
lookup[e] = true;
foreach (var e in l2)
if (!lookup.ContainsKey(e))
return false;
return true;
}
Usage:
Type[] l1 = { typeof(object), typeof(int), typeof(long), typeof(object) };
Type[] l2 = { typeof(int), typeof(long), typeof(object) };
var result = IsSameSet(l1, l2);
Console.WriteLine(result); // prints true
Exercise for the user:
Add an additional parameter to provide an IEqualityComparer<T> to be passed to the dictionary.
To compare any user defined customized types, we need to override Equals & GetHashCode.
Below is the code snippet you could refer to :
public class CustomizedDataType
{
private int field1;
private string field2;
public CustomizedDataType(int field1,string field2)
{
this.field1 = field1;
this.field2 = field2;
}
public override bool Equals(object obj)
{
CustomizedDataType dataType = obj as CustomizedDataType;
if (this.field1 == dataType.field1 && this.field2 == dataType.field2)
{
return true;
}
return false;
}
public override int GetHashCode()
{
return (this.field1.GetHashCode() + this.field2.GetHashCode());
}
Sample code to execute :
static void Main(string[] args)
{
//Test Data
List<CustomizedDataType> dataTypeContaineer1 = new List<CustomizedDataType>();
dataTypeContaineer1.Add(new CustomizedDataType(10,"Test10"));
dataTypeContaineer1.Add(new CustomizedDataType(11, "Test11"));
dataTypeContaineer1.Add(new CustomizedDataType(12, "Test12"));
//Test Data
List<CustomizedDataType> dataTypeContaineer2 = new List<CustomizedDataType>();
dataTypeContaineer2.Add(new CustomizedDataType(100, "Test10"));
dataTypeContaineer2.Add(new CustomizedDataType(11, "Test11"));
dataTypeContaineer2.Add(new CustomizedDataType(12, "Test120"));
//Checking if both the list contains the same types.
if (dataTypeContaineer1.GetType() == dataTypeContaineer2.GetType())
{
//Checking if both the list contains the same count
if (dataTypeContaineer1.Count == dataTypeContaineer2.Count)
{
//Checking if both the list contains the same data.
for (int index = 0; index < dataTypeContaineer1.Count; index++)
{
if(!dataTypeContaineer1[index].Equals(dataTypeContaineer2[index]))
{
Console.WriteLine("Mismatch # Index {0}", index);
}
}
}
}
}
Output :
You can use the C# keyword 'is' to see if an object is compatible with a given type.
http://msdn.microsoft.com/en-us/library/vstudio/scekt9xw.aspx
Assuming you mean that that two List<T> both have matching T, you could use:
private static Boolean MatchingBaseType(IEnumerable a, IEnumerable b)
{
return GetIListBaseType(a) == GetIListBaseType(b);
}
private static Type GetIListBaseType(IEnumerable a)
{
foreach (Type interfaceType in a.GetType().GetInterfaces())
{
if (interfaceType.IsGenericType &&
(interfaceType.GetGenericTypeDefinition() == typeof(IList<>) ||
interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
)
{
return interfaceType.GetGenericArguments()[0];
}
}
return default(Type);
}
You say count doesn't matter (though you're checking .Count()--why?) But this should return if the two lists have the same types in them.
I wrote this:
public static class EnumerableExtensions
{
public static int IndexOf<T>(this IEnumerable<T> obj, T value)
{
return obj
.Select((a, i) => (a.Equals(value)) ? i : -1)
.Max();
}
public static int IndexOf<T>(this IEnumerable<T> obj, T value
, IEqualityComparer<T> comparer)
{
return obj
.Select((a, i) => (comparer.Equals(a, value)) ? i : -1)
.Max();
}
}
But I don't know if it already exists, does it?
I'd question the wisdom, but perhaps:
source.TakeWhile(x => x != value).Count();
(using EqualityComparer<T>.Default to emulate != if needed) - but you need to watch to return -1 if not found... so perhaps just do it the long way
public static int IndexOf<T>(this IEnumerable<T> source, T value)
{
int index = 0;
var comparer = EqualityComparer<T>.Default; // or pass in as a parameter
foreach (T item in source)
{
if (comparer.Equals(item, value)) return index;
index++;
}
return -1;
}
The whole point of getting things out as IEnumerable is so you can lazily iterate over the contents. As such, there isn't really a concept of an index. What you are doing really doesn't make a lot of sense for an IEnumerable. If you need something that supports access by index, put it in an actual list or collection.
I would implement it like this:
public static class EnumerableExtensions
{
public static int IndexOf<T>(this IEnumerable<T> obj, T value)
{
return obj.IndexOf(value, null);
}
public static int IndexOf<T>(this IEnumerable<T> obj, T value, IEqualityComparer<T> comparer)
{
comparer = comparer ?? EqualityComparer<T>.Default;
var found = obj
.Select((a, i) => new { a, i })
.FirstOrDefault(x => comparer.Equals(x.a, value));
return found == null ? -1 : found.i;
}
}
The way I'm currently doing this is a bit shorter than those already suggested and as far as I can tell gives the desired result:
var index = haystack.ToList().IndexOf(needle);
It's a bit clunky, but it does the job and is fairly concise.
I think the best option is to implement like this:
public static int IndexOf<T>(this IEnumerable<T> enumerable, T element, IEqualityComparer<T> comparer = null)
{
int i = 0;
comparer = comparer ?? EqualityComparer<T>.Default;
foreach (var currentElement in enumerable)
{
if (comparer.Equals(currentElement, element))
{
return i;
}
i++;
}
return -1;
}
It will also not create the anonymous object
The best way to catch the position is by FindIndex This function is available only for List<>
Example
int id = listMyObject.FindIndex(x => x.Id == 15);
If you have enumerator or array use this way
int id = myEnumerator.ToList().FindIndex(x => x.Id == 15);
or
int id = myArray.ToList().FindIndex(x => x.Id == 15);
A bit late in the game, i know... but this is what i recently did. It is slightly different than yours, but allows the programmer to dictate what the equality operation needs to be (predicate). Which i find very useful when dealing with different types, since i then have a generic way of doing it regardless of object type and <T> built in equality operator.
It also has a very very small memory footprint, and is very, very fast/efficient... if you care about that.
At worse, you'll just add this to your list of extensions.
Anyway... here it is.
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
int retval = -1;
var enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
{
retval += 1;
if (predicate(enumerator.Current))
{
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
return retval;
}
}
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
return -1;
}
Hopefully this helps someone.
A few years later, but this uses Linq, returns -1 if not found, doesn't create extra objects, and should short-circuit when found [as opposed to iterating over the entire IEnumerable]:
public static int IndexOf<T>(this IEnumerable<T> list, T item)
{
return list.Select((x, index) => EqualityComparer<T>.Default.Equals(item, x)
? index
: -1)
.FirstOr(x => x != -1, -1);
}
Where 'FirstOr' is:
public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
{
return source.DefaultIfEmpty(alternate)
.First();
}
public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T alternate)
{
return source.Where(predicate)
.FirstOr(alternate);
}
Stumbled across this today in a search for answers and I thought I'd add my version to the list (No pun intended). It utlises the null conditional operator of c#6.0
IEnumerable<Item> collection = GetTheCollection();
var index = collection
.Select((item,idx) => new { Item = item, Index = idx })
//or .FirstOrDefault(_ => _.Item.Prop == something)
.FirstOrDefault(_ => _.Item == itemToFind)?.Index ?? -1;
I've done some 'racing of the old horses' (testing) and for large collections (~100,000), worst case scenario that item you want is at the end, this is 2x faster than doing ToList().FindIndex(). If the Item you want is in the middle its ~4x faster.
For smaller collections (~10,000) it seems to be only marginally faster
Heres how I tested it https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e
An alternative to finding the index after the fact is to wrap the Enumerable, somewhat similar to using the Linq GroupBy() method.
public static class IndexedEnumerable
{
public static IndexedEnumerable<T> ToIndexed<T>(this IEnumerable<T> items)
{
return IndexedEnumerable<T>.Create(items);
}
}
public class IndexedEnumerable<T> : IEnumerable<IndexedEnumerable<T>.IndexedItem>
{
private readonly IEnumerable<IndexedItem> _items;
public IndexedEnumerable(IEnumerable<IndexedItem> items)
{
_items = items;
}
public class IndexedItem
{
public IndexedItem(int index, T value)
{
Index = index;
Value = value;
}
public T Value { get; private set; }
public int Index { get; private set; }
}
public static IndexedEnumerable<T> Create(IEnumerable<T> items)
{
return new IndexedEnumerable<T>(items.Select((item, index) => new IndexedItem(index, item)));
}
public IEnumerator<IndexedItem> GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Which gives a use case of:
var items = new[] {1, 2, 3};
var indexedItems = items.ToIndexed();
foreach (var item in indexedItems)
{
Console.WriteLine("items[{0}] = {1}", item.Index, item.Value);
}
This can get really cool with an extension (functioning as a proxy), for example:
collection.SelectWithIndex();
// vs.
collection.Select((item, index) => item);
Which will automagically assign indexes to the collection accessible via this Index property.
Interface:
public interface IIndexable
{
int Index { get; set; }
}
Custom extension (probably most useful for working with EF and DbContext):
public static class EnumerableXtensions
{
public static IEnumerable<TModel> SelectWithIndex<TModel>(
this IEnumerable<TModel> collection) where TModel : class, IIndexable
{
return collection.Select((item, index) =>
{
item.Index = index;
return item;
});
}
}
public class SomeModelDTO : IIndexable
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Index { get; set; }
}
// In a method
var items = from a in db.SomeTable
where a.Id == someValue
select new SomeModelDTO
{
Id = a.Id,
Name = a.Name,
Price = a.Price
};
return items.SelectWithIndex()
.OrderBy(m => m.Name)
.Skip(pageStart)
.Take(pageSize)
.ToList();
Try this:
static int FindIndex<T>(this IEnumerable<T> a, Predicate<T> f) =>
a.TakeWhile(x => !f(x)).Count();
static int IndexOf<T>(this IEnumerable<T> a, T value) =>
a.FindIndex(x => EqualityComparer<T>.Default.Equals(x, value));
var i = new[] { 1, 2, 3 }.IndexOf(2); // 1