Why does IList<>.Reverse() not work like List<>().Reverse - c#

I have problem with List<T>.Reverse() and Reverse(this IEnumerable<TSource> source).
Look to the code:
// Part 1
List<int> list = new List<int> { 1, 2, 3 };
foreach (int x in list)
Console.Write(x);
Console.WriteLine();
list.Reverse();
foreach (int x in list)
Console.Write(x);
Console.WriteLine();
list.Reverse();
// Part2
IList<int> ilist = list;
foreach (int x in list)
Console.Write(x);
Console.WriteLine();
ilist.Reverse();
foreach (int x in ilist)
Console.Write(x);
Console.WriteLine();
ilist.Reverse();
My result:
123
321
123
123
because Reverse()-Part1 is List<T>.Reverse(), Reverse()-Part2 is Reverse(this IEnumerable<TSource> source)
I want execute List<int>.Reverse() in Part2 for IList<int>. How I can do it?

IList<int> doesn't have a Reverse method, so it uses the extension method. The only way to use List<T>.Reverse on your IList<int> reference is to cast or convert it to a List<int>. Casting will only work if you're sure that it's really a List<int> in the first place:
IList<int> abstractList;
var concreteList = (List<int>)abstractList;
concreteList.Reverse();
Another option would be to create a List<int> from your IList<int> instance, rather than assuming it already is a List<int>:
IList<int> abstractList;
var concreteList = new List<int>(abstractList);
concreteList.Reverse();
The reason that the Reverse extension method doesn't actually affect the underlying list is because it operates on IEnumerable<T>, which isn't necessarily writeable (none of the Enumerable extension methods make changes to the original collection, they return a new collection).
To use this version of Reverse, just use the product of the Reverse call, rather than the original list:
IList<int> abstractList;
IEnumerable<int> reversedList = abstractList.Reverse();

In the second example, you're using an extension method against IEnumerable<T>, and this is not mutating the original collection but rather producing a query that would result in a sequence of your original list in reverse order. That is to say, if you want to utilize the results of ilist.Reverse(), you would say
var reversedList = iList.Reverse();
// reversedList is IEnumerable<int>
// 'list' and 'ilist' are not changed
foreach (int item in reversedList)
Console.Write(item);

Related

LinkedList generic input

I wrote the folowing method :
// Merge two ArrayLists to the first one without duplication
public static void mergeIntoFirst(ArrayList array1, ArrayList array2)
{
if(array1==null | array2==null)
throw new ArgumentNullException();
if (array1 == array2)
return; //if they are pointing to the same array, then we can exit.
foreach (object obj in array2)
{
if (!array1.Contains(obj))
array1.Add(obj);
}
}
But now I want to change the my program to work with linkedList insted,
because arraylist doesn't work well with linq as far as I know...
But I need the input to be generc, and work with all linked list kind, just like here the ArrayList can contains all sort of objects.
(I'm using this method twice in my code, once with array of users, and the other with array of messages sent by the users)
I thought that using LinkedList<object> will solve it, since anything is object (exept int, chat double itc)
but it throws a casting exeption when running...
what shoul'd I do then?
Thanks!
Here's an implementation of your code that should work for any ICollection<T> of which both LinkedList<T> and List<T> implement. Note that you have to define the generic type on the method.
public static void MergeIntoFirst<T>(ICollection<T> c1, ICollection<T> c2)
{
if(c1==null || c2==null)
throw new ArgumentNullException();
if (c1 == c2)
return; //if they are pointing to the same array, then we can exit.
foreach (T item in c2)
{
if (!c1.Contains(item))
c1.Add(item);
}
}
Then you could use it like this.
List<int> l1 = new List<int> { 1, 2, 3 };
List<int> l2 = new List<int> { 2, 3, 4 };
MergeIntoFirst(l1, l2);
Console.WriteLine(string.Join(",", l1));
// outputs: 1, 2, 3, 4
Note that this is close to what Enumerable.Union does except that it would remove duplicates that exist in both collections and produces a new collection rather than mutating the first one.

How to Create A Merged Array from 3 Arrays with LINQ which is not zipped but based on Content Equality

I have three Arrays which should be merged into one result via Linq:
int?[] list1 = {0,1,2,3,4};
int?[] list2 = {2,3,4,5};
int?[] list3 = {3,4};
Result:
var result=
{
(0,null,null),
(1,null,null),
(2,2,null),
(3,3,3),
(4,4,4),
(null,5,null)
}
Let's start by defining our input in a little more generic terms: a list of a list of integers. Since we don't need to modify these collections, we'll use the simplest interface that gives us what we need, IEnumerable<T>. That means our input is going to be: IEnumerable<IEnumerable<int?>>. Our output is going to be the same.
So now, let's define a prototype for the method that will do the work:
public static IEnumerable<IEnumerable<int?>> Merge(IEnumerable<IEnumerable<int?>> source) { //... }
Immediately I've noticed something: we don't really need to use int? since all we care about is checking equality, and all types support that, so we can make this method generic, and support any type:
public static IEnumerable<IEnumerable<T>> Merge<T>(IEnumerable<IEnumerable<T>> source) { //... }
Now let's start with the implementation, first we will need to compute every distinct value from all the lists:
source.SelectMany(x=>x).Distinct()
Now, for each of those values we need to return a collection with an item for each item in the original 'super list':
source.SelectMany(x=>x).Distinct().Select(x=>source.Select(y=> //...
So what do we need in that final Select lambda? We have x as each distinct integer (or technically T), and y as each original collection. We want the value x if the y collection contains x, otherwise, null (or to allow value types too, default(T). We can do that with a ternary:
source.SelectMany(x=>x).Distinct().Select(x=>source.Select(y=>y.Contains(x)?x:default(T)));
Putting it all together:
public static IEnumerable<IEnumerable<T>> Merge<T>(this IEnumerable<IEnumerable<T>> source)
{
return source
.SelectMany(x=>x)
.Distinct()
.Select(x=>source
.Select(y=>y.Contains(x)?x:default(T)));
}
And you can call it like so:
int?[] list1 = {0,1,2,3,4};
int?[] list2 = {2,3,4,5};
int?[] list3 = {3,4};
var result = new []{ list1, list2, list3 }.Merge();
Console.WriteLine(string.Join(Environment.NewLine, result.Select(t=>string.Join(",", t))));
First put all your arrays into one:
var lists = new[] { list1, list2, list3 };
Now loop all possible numbers and check if check if they are contained in the appropriate arrays. If so, you can add that number to your result, otherwise add null:
var result = new List<List<int?>>();
for(int i = 0; i < 6; i++)
{
result.Add(new List<int?>());
for(int j = 0; j < 3; j++)
{
if(lists[j].Contains(i))
result[i].Add(i);
else
result[i].Add(null);
}
}
I suppose this is pretty straightforward. Doing this is linq will just overcomplicate things, looks ugly and is hard to debug and understand. I doubt it´s a good idea to do so.

How does this not lead to multiple enumerations of an IEnumerable?

I originally set my parameter type to be ICollection because I thought I would run into issues with multiple enumerations of an IEnumerable. However, Resharper suggested that I can convert the parameter type to be an IEnumerable. I ended up making a test to double check and it seems to work fine:
private static void Main(string[] args)
{
var nums = GetNums();
MultipleEnumerations(nums);
Console.ReadLine();
}
private static void MultipleEnumerations(IEnumerable<int> nums)
{
var otherNums = new List<int> {1, 2, 3, 4, 5};
var filtered = otherNums.Where(num => nums.Contains(num));
foreach (var num in filtered)
{
Console.WriteLine(num);
}
}
private static IEnumerable<int> GetNums()
{
yield return 4;
yield return 2;
}
How does this not lead to multiple enumerations of an IEnumerable?
This does lead to multiple enumerations of an enumerable.
If I change your code to this:
private static void MultipleEnumerations(IEnumerable<int> nums)
{
var otherNums = new List<int> { 1, 2, 3, 4, 5 };
var filtered = otherNums.Where(num =>
{
Console.WriteLine("{0}?", num);
return nums.Contains(num);
});
foreach (var num in filtered)
{
Console.WriteLine(num);
}
}
private static IEnumerable<int> GetNums()
{
Console.WriteLine("4!");
yield return 4;
Console.WriteLine("2!");
yield return 2;
}
...then I can get the following output:
1?
4!
2!
2?
4!
2!
2
3?
4!
2!
4?
4!
4
5?
4!
2!
You can see that for each otherNums being tested it is running through nums (which is GetNums) each time. When testing 4 it only takes the first number of nums, but otherwise it is iterating it fully 5 times.
If you're wondering why .Where and the foreach both don't iterate thru the otherNums it is because .Where is run with a deferred execution model - it's only when you do a foreach or something like .ToList() that it executes.
Resharper probably suggested changing the type of the parameter to IEnumerable, because there is no caller who would pass other type than (derived from) IEnumerable, Resharper suggested the simplest type.
ICollection adds only a few properties and methods to IEnumerable from which it derives, most importantly the Count property.
According to this table, the GetEnumerator() method has the same complexity for most classes implementing ICollection.

'Cropping' a list in c#

Given a Generic IList of some type, which contains a number of items, is there any way of 'cropping' this list, so that only the fist x items are preserved, and the rest discarded?
If you can use Linq, it's just a matter of doing
// Extraact the first 5 items in myList to newList
var newList = myList.Take(5).ToList();
// You can combine with .Skip() to extract items from the middle
var newList = myList.Skip(2).Take(5).ToList();
Note that the above will create new lists with the 5 elements. If you just want to iterate over the first 5 elements, you don't have to create a new list:
foreach (var oneOfTheFirstFive in myList.Take(5))
// do stuff
The existing answers create a new list containing a subset of items from the original list.
If you need to truncate the original list in-place then these are your options:
// if your list is a concrete List<T>
if (yourList.Count > newSize)
{
yourList.RemoveRange(newSize, yourList.Count - newSize);
}
// or, if your list is an IList<T> or IList but *not* a concrete List<T>
while (yourList.Count > newSize)
{
yourList.RemoveAt(yourList.Count - 1);
}
you have a very simple way to:
IList<T> list = [...]; //initialize
IList<T> newList = new List<T>(max);
for (i=0; i<max; i++) newList.Add(list[i]);
Note: max MUST be less or equal then list length (otherwise you get IndexOutOfBoundsException)
If you need to do it just with the IList<T> interface, then something like this is the solution:
for (int i = list.Count - 1; i >= numberOfElementsToKeep; --i) {
list.RemoveAt(i);
}
Working backwards from the end of the list here, in order to avoid moving around data which will be deleted in subsequent loop iterations.

How to retrieve the elements in list without using foreach loop in C#?

How can I retrieve the elements from a list without using a foreach loop?
var list = new List<int> { 1, 2, 3, 4, 5 };
for (int i = 0; i < list.Count(); i++)
{
var element = list[i];
}
or
var list = new List<int> { 1, 2, 3, 4, 5 };
using (var enumerator = list.GetEnumerator())
{
while (enumerator.MoveNext())
{
var element = enumerator.Current;
}
}
You say list, but you don't specify the List<T> class as this answer assumes (also, it might be added that that answer uses the Count() extension method. Since you know the type is of List<T> it's better to use the Count property).
If you are always working with the IList<T> interface implementation, then using a for loop that iterates an index and then accesses the indexer with that value will work fine.
However, if you are dealing with IEnumerable<T> implementations, that will not always work. Rather, you have to do the following:
// Get the IEnumerator<T> from the list.
IEnumerator<T> enumerator = list.GetEnumerable();
// Dispose if necessary.
using (enumerator as IDisposable)
{
// Cycle while there are items.
while (enumerator.MoveNext())
{
// Work with enumerator.Current here.
}
}
This is how the compiler expands the foreach statement when it is compiled. Basically, since IEnumerable<T> implementations can implement IDisposable, it prepares for that eventuality by trying to cast to IDisposable. If it cannot, then the using statement just doesn't do anything on exit.
When using foreach on arrays, the compiler will expand to a loop which accesses the items by index (assuming you are working with an array instance directly), not the enumerator approach above.
If you're trying to avoid this type of loop:
foreach(var item in list) {};
...then you can use Linq or Lambda expressions to search and retrieve from the list.
For example:
using System.Linq;
// ... with Lambda
var ints = new List<int>(){1,2,3,4,5};
var evenInts = ints.ForEach(i => i % 2 == 0);
// with straight Linq-to-objects:
var oddInts = from i in ints
where i % 2 == 1
select i;

Categories