Adding an IList item to a particular index number - c#

Our Client's database returns a set of prices in an array, but they sometimes don't include all prices, i.e., they have missing elements in their array. We return what we find as an IList, which works great when we retrieve content from the database. However, we are having difficulties setting the elements in the proper position in the array.
Is it possible to create an IList then add an element at a particular position in the IList?
var myList = new List<Model>();
var myModel = new Model();
myList[3] = myModel; // Something like what we would want to do

Use IList<T>.Insert(int index,T item)
IList<string> mylist = new List<string>(15);
mylist.Insert(0, "hello");
mylist.Insert(1, "world");
mylist.Insert(15, "batman"); // This will throw an exception.
From MSDN
If index equals the number of items in the IList, then item is appended to the list.
In collections of contiguous elements, such as lists, the elements that follow the insertion point move down to accommodate the new element. If the collection is indexed, the indexes of the elements that are moved are also updated. This behavior does not apply to collections where elements are conceptually grouped into buckets, such as a hash table.

Use IList.Insert Method.

Lists grow dynamically to accommodate items as they are added. You would have to initialize the list with a predefined size. The easiest way I can think of to do that would be:
var myList = new Model[100].ToList();
That'll give you a list with 100 items, all null. You're then free to assign a value to myList[3].
Note that in your code you are trying to instantiate an IList<Model> which isn't possible - you need a concrete type (like List<Model>) rather than an interface.

It will insert and resize if needed
public static IList<T> InsertR<T>(this IList<T> ilist, int index, T item) {
if (!(index < ilist.Count)) {
T[] array = Array.CreateInstance(typeof(T), index + 1) as T[];
ilist.CopyTo(array, 0);
array[index] = item;
if (ilist.GetType().IsArray) {
ilist = array;
} else {
ilist = array.ToList();
}
} else
ilist[index] = item;
return ilist;
}
or
public static IList InsertR<T>(this IList ilist, int index, T item) {
if (!(index < ilist.Count)) {
T[] array = Array.CreateInstance(typeof(T), index + 1) as T[];
ilist.CopyTo(array, 0);
array[index] = item;
if (ilist.GetType().IsArray) {
ilist = array;
} else {
ilist = array.ToList();
}
} else
ilist[index] = item;
return ilist;
}

Related

C# how to avoid duplicates in a list?

What way I could use to avoid duplicates in a list?
One way is when I will add a new item, check first if the element exists, but this make me use more code and iterate all the list to check if it exists.
Another way I could use a hashset, that if I try to add a new item, itself check if the item exists, if not, it will add the new item, if exists, then do nothing.
But I know that the hashset is less efficient, need more resources than a list, so I don't know if using a hashset to avoid duplicates it is a good use of the hashset.
There are any other alternative?
Thanks.
You can achieve this in a single line of code :-
List<long> longs = new List<long> { 1, 2, 3, 4, 3, 2, 5 };
List<long> unique = longs.Distinct().ToList();
unique will contains only 1,2,3,4,5
You cannot avoid duplicates in List. No way - there is no verification of items.
If you don't bother with order of items - use HashSet.
If you want to preserve order of items (actually there is a little ambiguity - should item appear at index of first addition or at index of last addition). But you want to be sure that all items are unique, then you should write your own List class. I.e. something which implements IList<T> interface:
public class ListWithoutDuplicates<T> : IList<T>
And you have different options here. E.g. you should decide what is more important for you - fast addition or memory consumption. Because for fast addition and contains operation you should use some hash-based data structure. Which is unordered. Here is sample implementation with HashSet for storing hashes of all items stored in the internal list. You will need following fields:
private readonly HashSet<int> hashes = new HashSet<int>();
private readonly List<T> items = new List<T>();
private static readonly Comparer<T> comparer = Comparer<T>.Default;
Adding items is simple (warning: no null-checks here and further) - use item hash code to quickly O(1) check if it's already added. Use same approach for removing items:
public void Add(T item)
{
var hash = item.GetHashCode();
if (hashes.Contains(hash))
return;
hashes.Add(hash);
items.Add(item);
}
public bool Remove(T item)
{
var hash = item.GetHashCode();
if (!hashes.Contains(hash))
return false;
hashes.Remove(item.GetHashCode());
return items.Remove(item);
}
Some index-based operations:
public int IndexOf(T item)
{
var hash = item.GetHashCode();
if (!hashes.Contains(hash))
return -1;
return items.IndexOf(item);
}
public void Insert(int index, T item)
{
var itemAtIndex = items[index];
if (comparer.Compare(item, itemAtIndex) == 0)
return;
var hash = item.GetHashCode();
if (!hashes.Contains(hash))
{
hashes.Remove(itemAtIndex.GetHashCode());
items[index] = item;
hashes.Add(hash);
return;
}
throw new ArgumentException("Cannot add duplicate item");
}
public void RemoveAt(int index)
{
var item = items[index];
hashes.Remove(item.GetHashCode());
items.RemoveAt(index);
}
And left-overs:
public T this[int index]
{
get { return items[index]; }
set { Insert(index, value); }
}
public int Count => items.Count;
public bool Contains(T item) => hashes.Contains(item.GetHashCode());
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
That's it. Now you have list implementation which will add item only once (first time). E.g.
var list = new ListWithoutDuplicates<int> { 1, 2, 1, 3, 5, 2, 5, 3, 4 };
Will create list with items 1, 2, 3, 5, 4. Note: if memory consumption is more important than performance, then instead of using hashes use items.Contains operation which is O(n).
BTW What we just did is actually a IList Decorator
A List is a data-structure that may contain duplicates. Duplicate elements are disambiguated by their index.
One way is when I will add a new item, check first if the element exists, but this make me use more code and iterate all the list to check if it exists.
This is possible, but it is error-prone and slow. You will need to iterate through the entire list every time you want to add an element. It is also possible that you will forget to check somewhere in your code.
Another way I could use a hashset, that if I try to add a new item, itself check if the item exists, if not, it will add the new item, if exists, then do nothing.
This is the preferred way. It is best to use the standard library to enforce the contraints that you want.
But I know that the hashset is less efficient, need more resources than a list, so I don't know if using a hashset to avoid duplicates it is a good use of the hashset.
The efficiency depends on what you are trying to do; see https://stackoverflow.com/a/23949528/1256041.
There are any other alternative?
You could implement your own ISet using List. This would make insertion much slower (you would need to iterate the whole collection), but you would gain O(1) random-access.
The hashset is the best way to check if the item exist because it's O(1).
So you can insert the items both in a list and in hashset
and before inserting a new item you check if it's exist in the hashset.

Array, List, IEnumerable, CustomList class cast to one and iterate threw them

I am trying to figure out a way that tells me if a certain type is an array/list/ienumerable/collection ... I dont care what kind of it is even CustomLists so something like
FooList<T> : IList<T>
FooList : IList
or stuff like that.
I kinda hoped that a simple type.IsArray would be enough but sadly this isnt the case.
I need a way to check if its one of the above types and then check what the underlying type is, and than cast it to a Indexed based collection, where I can loop through the entries.
For a simple array this is all I need:
if (obj.GetType().IsArray)
{
var elementType = obj.GetType().GetElementType();
if (elementType.IsPrimitive == false)
{
var array = (Array)obj;
}
}
This should work for every collection, there could possible be.
Edit:
As recommended below, I should as/is to IEnumerable but with IEnumerable I have the problem that the I cannot set certain object inside this IEnumerable.
With array I have used the method array.SetValue(obj, index) which works fine.
When I loop threw the IEnumerable and try to set one entry like this:
var list = obj as IEnumarble;
if (list != null)
{
foreach (var item in list)
{
item = new object();
}
}
I am getting the following message:
Readonly local variable cannot be used as an assignment target.
You can try to cast it with the as operator:
var enumerable = list as IEnumerable;
if (enumerable != null)
{
foreach (object item in enumerable)
{
// ...
}
}
However, if you need to modify it you have to recreate it. For example by using a list which you fill in the loop. Then reassign it to the original variable.
Or you could check if the type is a ILIst in the first place (like an array or list), then you can use it`s indexer:
var ilist = list as IList;
if (ilist != null)
{
for (int i = 0; i < ilist.Count; i++)
{
ilist[i] = "new value";
}
}
IIRC, you could do a simple inheritence check for the enumerable interface via
if (FooList is IEnumerable)
// We have a List
You can also use Linq and do a
if (FooList.ToList().Count > 1)
// We have a List
But this would be rather unconventional.

'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.

Problem with custom IComparer for List (sort) - c#

can anyone help, i have problem doing a sort, I thought i had it sorted but appears not to be working.
I have a List which stores the following values
8,6,10,11,7
I also have another List (accessories in my class and it has a propert called accessoryId current the classes are in the order of id which is currenty 6,7,8,10,11)
Hence i need to sort them from 6,7,8,10,11 to the order used from the simple list which is 8,6,10,11,7
I have my icomparable (see below) and i am calling like this - it does enter but something is wrong BECAUSE the list still has all my classes but is still in the order of 6,7,8,10,11
// accesories is the IList<Accessories> (hence why i am use ToList)
// and sortOrder is the simple int list list<int>
accesories.ToList().Sort(new ItemTpComparer(sortOrder));
class ItemTpComparer : IComparer<Accessories>
{
private IList<int> otherList;
public ItemTpComparer(IList<int> otherList)
{
this.otherList = otherList;
}
#region IComparer<Accessories> Members
public int Compare(Accessories x, Accessories y)
{
if (otherList.IndexOf(x.AccessoryId) > otherList.IndexOf(y.AccessoryId))
return 1;
else if (otherList.IndexOf(x.AccessoryId) < otherList.IndexOf(y.AccessoryId))
return -1;
else
return 0;
// tried below also didn't work
//return otherList.IndexOf(x.AccessoryId) - otherList.IndexOf(y.AccessoryId);
The comparer is correct (even the commented single line version). The problem is ToList() creates a new List containing a copy of elements in the IEnumerable<T> object so basically, you are creating a new list, sorting it and throwing it away.
var sortedList = accesories.ToList();
sortedList.Sort(new ItemTpComparer(sortOrder));
for which I'd suggest replacing with:
var sortedList = accessories.OrderBy(sortOrder.IndexOf).ToList();
this way, no comparer implementation would be necessary. You could also sort in the descending order easily:
var sortedList = accessories.OrderByDescending(sortOrder.IndexOf).ToList();
If the object is really List<Accessories>, you could also sort it in place:
((List<Accessories>)accessories).Sort(new ItemTpComparer(sortOrder));
Mehrdad showed you why the list was not sorted. I want to address the performance of the comparer, and also the issue with less sorting items than sorted items.
Using IndexOf on a list to locate the index is quite inefficient. I has to loop through the items in the list to find the right one. Use a dictionary as lookup instead, that way you only loop through the items once:
class ItemTpComparer : IComparer<Accessories> {
private Dictionary<int, int> index;
public ItemTpComparer(IList<int> otherList) {
index = new Dictionary<int, int>();
for (int i = 0; i < otherList.Count; i++) {
index.Add(otherList[i], i);
}
}
public int Compare(Accessories x, Accessories y) {
return index[x.AccessoryId].CompareTo(index[y.AccessoryId]);
}
}
If you want to allow the list of value to sort by to be shorter than the list of items to sort, you check if the value exists in the dictionary:
public int Compare(Accessories x, Accessories y) {
int xIndex, yIndex;
if (!index.TryGetValue(x.AccessoryId, out xIndex)) xIndex = int.MaxValue;
if (!index.TryGetValue(y.AccessoryId, out yIndex)) yIndex = int.MaxValue;
return xIndex.CompareTo(yIndex);
}

How can I convert a list<> to a multi-dimensional array?

I have the following method signature:
public void MyFunction(Object[,] obj)
I create this object:
List<List<Object>> obj = new List<List<Object>>;
Is there an easy way I can convert this to an Object[,]?
UPDATE:
The fact is I like to use Lists because I can easily add a new item. Is there a way I can declare my List<> object to fit this need? I know the number of columns in my Object[,] but not the number of rows.
No. In fact, these aren't necessarily compatible arrays.
[,] defines a multidimensional array. List<List<T>> would correspond more to a jagged array ( object[][] ).
The problem is that, with your original object, each List<object> contained in the list of lists can have a different number of objects. You would need to make a multidimensional array of the largest length of the internal list, and pad with null values or something along those lines to make it match.
You're not going to get a very simple solution for this (i.e. a few lines). LINQ/the Enumerable class isn't going to help you in this case (though it could if you wanted a jagged array, i.e. Object[][]). Plain nested iteration is probably the best solution in this case.
public static T[,] To2dArray(this List<List<T>> list)
{
if (list.Count == 0 || list[0].Count == 0)
throw new ArgumentException("The list must have non-zero dimensions.");
var result = new T[list.Count, list[0].Count];
for(int i = 0; i < list.Count; i++)
{
for(int j = 0; j < list[i].Count; j++)
{
if (list[i].Count != list[0].Count)
throw new InvalidOperationException("The list cannot contain elements (lists) of different sizes.");
result[i, j] = list[i][j];
}
}
return result;
}
I've included a bit of error handling in the function just because it might cause some confusing errors if you used it on a non-square nested list.
This method of course assumes that each List<T> contained as an element of the parent List is of the same length. (Otherwise you really need to be using a jagged array.)
Here is a solution using Linq's Aggregate extension.
Note that the below does not check, nor is concerned if it gets a jagged sub list, it uses the max size of all the sublists and fills in according to the current list. If that is a concern one could add a check to the if to check for the same count amongst all the sub lists.
public static T[,] To2DArray<T>(this List<List<T>> lst)
{
if ((lst == null) || (lst.Any (subList => subList.Any() == false)))
throw new ArgumentException("Input list is not properly formatted with valid data");
int index = 0;
int subindex;
return
lst.Aggregate(new T[lst.Count(), lst.Max (sub => sub.Count())],
(array, subList) =>
{
subindex = 0;
subList.ForEach(itm => array[index, subindex++] = itm);
++index;
return array;
} );
}
Test / Usage
var lst = new List<List<string>>() { new List<string>() { "Alpha", "Beta", "Gamma" },
new List<string>() { "One", "Two", "Three" },
new List<string>() { "A" }
};
var newArray = lst.To2DArray();
Result:
To be blunt, the answer is no, not easily.
Perhaps you would like to edit your question to give us more background about why these declarations are needed and we can help you with your root problem?
Re your update:
I assume you cannot change the function you need to pass this into.
I don't see why you cannot just use an object[,] to begin with. This is my recommendation.
I doubt this will help you in your situation, but it might make some of the array working easier on you to start with. Do you know about the .ToArray() method on a List?

Categories