Instantiating a List - c#

If I want to instantiate a array the syntax is
int[] items = new int[] { 1, 2, 3 };
and the shortcut is
int[] items = {1,2,3};
Now I want to do the same to a List.
Question:
why does this work:
List<int> items = new int[] { 1, 2, 3 }.ToList();
but not this:
List<int> items = { 1, 2, 3 }; //invalid
or
List<int> items = { 1, 2, 3 }.ToList(); //invalid

The syntax
int[] array = {1,2,3};
is special syntactic sugar for array initialization. {1,2,3} is not itself an array yet.
This line
List<int> list = new[] {1,2,3}.ToList();
works because the expression new[] {1,2,3} returns an int[], which implements IEnumerable<int> and so you can call ToList() on it.
In the specs that's the difference between 12.6 Array initializers and 7.5.10.2 Array creation expressions.
List<T> has a constructor that takes an IEnumerable<T> as argument to initialize the list's content with. So you can either call
List<int> list = new List<int>(new[]{1,2,3});
or use the collection initializer syntax sugar:
List<int> list = new List<int> {1,2,3};
which is converted by the compiler to something like this:
var tmp = new List<int>();
tmp.Add(1);
tmp.Add(2);
tmp.Add(3);
List<int> list = tmp;

Related

Remove Duplicate entries from a 2D list in CSharp

How do I remove duplicate entries from a 2D list in C#.
here is my code.
HashSet<List<int>> set = new HashSet<List<int>>();
set.Add(new List<int>(){1,-2,-1,2});
set.Add(new List<int>(){3,-2,1,1});
set.Add(new List<int>() {1,-2,-1,2}); //duplicate entry
but the result i get from this has the duplicate entry {{1,-2,-1,2},{3,-2,1,1},{1,-2,-1,2}} i also tried using set.Distinct().ToList() but i still get duplicates in my result.
please can someone point me to a neat way to get this done using HashSet. I don't want to compare each sequence in the list because that adds some time complexity to my code.
Thanks for your help in anticipation.
You can write your own implementation of IEqualityComparer<List<int>> (and use a SequenceEqual method for list equality)
public class ListComparer : IEqualityComparer<List<int>>
{
public bool Equals(List<int> x, List<int> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<int> obj)
{
return obj.Aggregate(19, (current, item) => current ^ item.GetHashCode());
}
}
Then pass its instance to Distinct method
var set = new HashSet<List<int>>
{
new List<int>() {1, -2, -1, 2}, new List<int>() {3, -2, 1, 1}, new List<int>() {1, -2, -1, 2}
};
var result = set.Distinct(new ListComparer());
It allows you to remove {1,-2,-1,2} duplicated entry.
You can also pass ListComparer instance to HashSet constructor and get rid of adding a duplicate lists
var set = new HashSet<List<int>>(new ListComparer())
{
new List<int>() {1, -2, -1, 2}, new List<int>() {3, -2, 1, 1}, new List<int>() {1, -2, -1, 2}
};
In example above set will contain only two items without duplicated lists
you can create an implementation of IEqualityComparer and pass it to Distinct function.
in you implementation of IEqualityComparer order the list and check for except (or intersect) if there is any then the two list is not equals.
you can also do a simple loop through each list, and check for item in same index if number of same keys are equal to your list length then eliminate on of them.

IEnumerable - exclude using an array

Does anyone know if it's possible to create a new IEnumerable by using an array parameter to exclude values.
For instance, below is an example of how I imagine it would look.
class Item
{
public int id { get; set; }
public string name { get; set; }
}
IEnumerable looks like this:
item1 {id = 1}
item2 {id = 2}
item3 {id = 3}
I want to create a new IEnumerable but exclude the id numbers in the array.
Made up code to suggest idea:
Int32[] arrayList = {1,2};
var newIEnumerable = _exisitingIEnumerable.Where(o => (o.id NOT IN arrayList));
Looking at your question again, when the element type of _exisitingIEnumerable is not the same as that of arrayList, you will need to use Where to filter out the elements of arrayList
_exisitingIEnumerable.Where(o => !arrayList.Contains(o.Id))
Original answer:
_exisitingIEnumerable.Except(arrayList)
will return the distinct elements from _exisitingIEnumerable that are not in arrayList
If you need duplicates, you can use
_exisitingIEnumerable.Where(o => !arrayList.Contains(o))
What's wrong with the approach you suggested in the question? You can use Where and check if the array contains the value. Below the example using List as a target collection:
var myList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] myArray = { 1, 2, 3 };
var result = new List<int>(myList.Where(n => !myArray.Contains(n)));

SortedList<> and its strange setters

I initially had some code, which when simplified, looks like this:
var planets = new List<Planet>
{
new Planet {Id = 1, Name = "Mercury"},
new Planet {Id = 2, Name = "Venus"},
};
I got into a scenario where the list was being populated all at once, but the reads weren't fast enough. And so, I changed this to use a SortedList instead.
I later realized that I could rewrite it like this
var planets = new SortedList<int, Planet>
{
{1, new Planet {Id = 1, Name = "Mercury"}},
{2, new Planet {Id = 2, Name = "Venus"}},
//in my actual code, i am reading the ids from a db
};
But before I got to this approach, I had the code written like this
var planets = new SortedList<int, Planet>
{
Keys = {1, 2},
Values =
{
new Planet {Id = 1, Name = "Mercury"},
new Planet {Id = 2, Name = "Venus"},
}
};
which gives me this exception
System.NotSupportedException: This operation is not supported on SortedList
nested types because they require modifying the original SortedList.
at System.ThrowHelper.ThrowNotSupportedException(ExceptionResource resource)
at System.Collections.Generic.SortedList`2.KeyList.Add(TKey key)
which I found to be very strange, coz IMHO, I wasn't really modifying the "original SortedList" as it claims, and what "nested types" is it talking about? Is it the list of keys internal to the SortedList?
I see then that the Keys and Values properties in SortedList don't actually have setters. They are read-only properties, and yet, I don't get a compile-time error. I am allowed to make a set call, as I can see in the stack trace with KeyList.Add. I feel the only reason why this fails is because of an explicit check within SortedList, which seems bizarre to me!
For instance
var str = new String {Length = 0}; gives me a compile-time error as expected, since Length is a read-only property, as does planets.Keys = null;
Someone please tell me - what simple fact am I overlooking here?
The code that you've written is comparable to this:
var planets = new SortedList<int, Planet>();
planets.Keys.Add(1);
planets.Keys.Add(2);
planets.Values.Add(new Planet { Id = 1, Name = "Mercury" });
planets.Values.Add(new Planet { Id = 2, Name = "Venus" });
SortedList requires that you add the value and key at the same time via SortedList<TKey, TValue>.Add(TKey key, TValue value) method, so that it can sort the value by the key. The implementation of the IList<T> which is used for Keys and Values internally does not support adding a respective key or value independently via the IList<T>.Add(T value) method.
You should be able to reproduce this error by calling Keys.Add(...) or Values.Add(...)
My initial query about the SortedList has now minimized to this concern about array, collection & object initializers, and the way the compiler interprets them differently. Thanks to #Haney again for the first answer to guide me towards this point of view, and to ILSpy for these insights.
Here are some array and collection initializers:
int[] a = { 1, 2, 3 };
int[] b = new int[] { 1, 2, 3 };
IList<int> c = { 1, 2, 3 };
IList<int> d = new int[] { 1, 2, 3 };
They all look kind of similar. Here, the compiler produces the exact same output for a & b. For c, we will get this compile-time error:
Can only use array initializer expressions to assign to array types.
Try using a new expression instead.
which makes sense since we shouldn't use array initializers for collections. But then, d produces the exact same result as a & b. And I thought that was an array initializer as well. Apparently not.
Now consider this class
class MyCollectionContainer
{
public int[] MyIntArray { get; set; }
public IList<int> MyList { get; set; }
}
and this code that operates on it
var containerA = new MyCollectionContainer { MyIntArray = { 1, 2, 3 } };
var containerB = new MyCollectionContainer { MyIntArray = new int[]{ 1, 2, 3 } };
var containerC = new MyCollectionContainer { MyList = { 1, 2, 3 } };
var containerD = new MyCollectionContainer { MyList = new int[]{ 1, 2, 3 } };
containerA gives this compile-time error:
Cannot initialize object of type 'int[]' with a collection initializer
For containerB, the compiler effectively converts it into this code:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyIntArray = new int[] {1, 2, 3};
For containerD, its pretty much the same, barring the fact that its another property that gets initialized:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList = new int[] {1, 2, 3};
For containerC, the compiler morphs it into:
MyCollectionContainer myCollectionContainer = new MyCollectionContainer();
myCollectionContainer.MyList.Add(1);
myCollectionContainer.MyList.Add(2);
myCollectionContainer.MyList.Add(3);
This results in a run-time NullReferenceException since MyList is not initialized.
This means the only valid ways to initialize the collection container object here is containerB and containerD. To me, this clearly shows that object initializers are different when compared to array & collection initializers, in the way the compiler interprets them.

C#, Lambda to transfer elements from one list to another

Is there a simple lambda expression to extract elements from one list and put them into another? without LINQ?
for example to map, a source list of elements T to another list (or return a list) with the string name for each element in the source.
Update...with pseudocode.
List<int> intList = new List<int>() { 1, 2, 3};
List<string> stringList = new List<string>(intList.ToArray((i) => intList[i].ToString())); // this doesn't work obviously
stringList should be {"1", "2", "3"}
List<T>.ConvertAll() provides a straightforward way to change types without LINQ.
In your case...
List<string> stringList = intList.ConvertAll(i => i.ToString());

Removing items in Jagged array using index

I want to remove items of a Jagged array using indizes.
int[] toRemove; (e.g, {0, 1})
int[][] MainArray (e.g. { [0] {...}, [1] {...}, [2] {...}}
Expected result
int[][] result (e.g. {[2] {...}}
From the MainArray how to remove the items which having indexes from the toRemove list?
Is there an efficient way using LINQ?
Hopefully this gives the expected result:
var notInToRemove = MainArray
.Where((arr ,index) => !toRemove.Contains(index)).ToArray();
You could use the ElementAt method instead of the Remove method if all you want is the data that is not to be there in the toRemove set.
int[] toRemove = {0,1};
int[][] mainArray = new int[3][];
mainArray[0] = new int[]{0,0,0};
mainArray[1] = new int[]{1,1,1};
mainArray[2] = new int[]{2,2,2};
var result = mainArray.ElementAt(2); // This value 2 is found as all indexes of mainArray except the values in toRemove
//(Code would look like this :
Enumerable.Range(0, mainArray.Length).Except(toRemove);

Categories