Reputation: 31
I have a List of objects and I have to write a method which extracts some elements in accordance to an input parameter which is an array of Type. The function must return an array of elements from the list each of them being an instance of one element from the array of types. At the same time those elements must be removed from the container list but only if they ALL do exist in the list. Type comparison is achieved by Type.IsInstanceOfType(o)
method.
class A {}
class B : A {}
class C : A {}
class D : A {}
public static A[] ExtractElements (List<A> list, Type[] specifiers) {...}
Type[] specifiers1 = new Type[2] {typeof(D), typeof(B)};
Type[] specifiers2 = new Type[3] {typeof(C), typeof(A), typeof(D)};
Type[] specifiers3 = new Type[2] {typeof(A), typeof(A)};
Type[] specifiers4 = new Type[2] {typeof(C), typeof(C)};
List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result1 = ExtractElements (list, specifiers1);
list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result2 = ExtractElements (list, specifiers2);
list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result3 = ExtractElements (list, specifiers3);
list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result4 = ExtractElements (list, specifiers4);
Results of this code fragment would be:
result1 is {D, B}, list is {A, C, A}
result2 is {C, B, D}, list is {A, A}
result3 is {B, A}, list is {D, C, A}
result4 is empty array, list is {B, A, D, C, A}
As a seperate effort would it be possible to write a similar ExtractElements method which only returns non-empty array if the list contains items of requested types whose order in the list corresponds to the order of elements in the types input array like the following
Type[] specifiers5 = new Type[2] {typeof(B), typeof(D)};
Type[] specifiers6 = new Type[2] {typeof(C), typeof(B)};
List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result5 = ExtractElements (list, specifiers5);
list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result6 = ExtractElements (list, specifiers6);
Results of this code fragment would be:
result5 is {B, D}, list is {A, C, A}
result6 is empty array, list is {B, A, D, C, A}
I know that LINQ
is the way to implement this but unfortunately I have no experience with it.
Upvotes: 3
Views: 3133
Reputation: 1840
This should get you everything you wanted. I added an optional argument to the ExtractElements
method that lets you enable/disable the order matching.
public static A[] ExtractElements (List<A> list, Type[] specifiers, bool orderMatters = false)
{
var allFound = true;
var listBackup = list.ToList(); // Make a backup copy
var returnList = new List<A>();
var earliestMatch = 0;
foreach (var spec in specifiers)
{
var item = list.FirstOrDefault (i => spec.IsAssignableFrom(i.GetType()));
if (item != null)
{
var matchPosition = list.IndexOf(item);
if (orderMatters && matchPosition < earliestMatch) // we have an out of order match
{
allFound = false;
break;
}
earliestMatch = matchPosition;
list.Remove(item);
returnList.Add(item);
}
else
{
allFound = false;
break;
}
}
if(!allFound)
{
// Can't just assign list to listBackup because we have to update the
// underlying values not the reference that was passed to the function.
list.Clear();
listBackup.ForEach(i => list.Add(i));
returnList.Clear();
}
return returnList.ToArray();
}
I suggest grabbing a copy of LinqPad to help you test any LINQ
statements and to learn LINQ
in general.
Hope this helps.
Upvotes: 0
Reputation: 1709
How about:
public IEnumerable<TType> ExtractElements<TType>(IEnumerable<TType> list, IEnumerable<Type> specifiers) {
var specifiersList = specifiers.ToList();
return list.Where(t => specifiersList.Any(s => s.IsAssignableFrom(t.GetType())));
}
var specifiers5 = new[] {typeof(B), typeof(D)};
var list = new List<A> {new B(), new A(), new D(), new C(), new A()};
// you can call ToArray() if you want but ForEach won't be available on that
// and you'll need a standard foreach() loop
var result5 = ExtractElements(list, specifiers5).ToList();
result5.ForEach(Console.WriteLine);
For more information on Type.IsAssignableFrom()
Upvotes: 1