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.
Related
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.
I'm trying to make a list be able to added to by plusing an item into it.
Target use of code I'm trying to make happen:
List<int> numbers = new List<int>();
numbers += 10;
What I've tried.
"operator +" should overload + and "this IList" should extend generic IList.
public static IList<T> operator +<T>(this IList<T> list, T element)
{
list.Add(element);
return list;
}
It's not working however, red underlines everywhere over it in visual studios 2012.
What am I doing wrong? Is this not possible? Why could this work for a standard class but not a generic class?
Operators can only be overloaded in the definition of the class. You can't override them from outside by using extension methods.
Also, at least one of the parameters must be of the same type as the class.
So the best you can do is something like:
public class CustomList<T> : List<T>
{
public static CustomList<T> operator +(CustomList<T> list, T element)
{
list.Add(element);
return list;
}
}
That you can then use like:
var list = new CustomList<int> { 1, 2 };
list += 3;
Console.WriteLine(string.Join(", ", list)); // Will print 1, 2, 3
The accepted answer above works better for the question asked, but in addition to that if someone needs to add two lists of any type and preserve the original lists. I should probably have posted this on a different topic, but doing search on "overloading + operator in c#" brings up this as top results. It may help someone hopefully.
void Main()
{
List<string> str = new List<string> {"one", "two", "three"};
List<string> obj = new List<string> {"four", "five", "six", "seven"};
foreach ( string s in str + obj ) Console.Write(s + ", ");
}
public class List<T> : System.Collections.Generic.List<T>
{
public static List<T> operator +(List<T> L1, List<T> L2)
{
List<T> tmp = new List<T>() ;
foreach ( T s in L1 ) tmp.Add(s);
foreach ( T s in L2 ) tmp.Add(s);
return tmp ;
}
}
//one, two, three, four, five, six, seven,
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.
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);
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?