Iterate over multiple lists - c#

Given a bunch of lists, I need to iterate over them simultaneously. Suppose I have three of them: list1, list2, and list3.
What I found so far is the following:
foreach (var tuple in list1.Zip(list2, (first, second) => new { object1 = first, object2 = second })
.Zip(list3, (first, second) => new { object1 = first.object1, object2 = first.object2, object3 = second }))
{
//do stuff
}
This works fine and is quite readable, unless the number of lists is not big. I know how to extend it further to 4, 5,.... lists, but if I zip 10 of them, the code would be extremely long. Is there any possibility to refactor it? Or would I need other solution than Zip function?

With a help of a bit of code generation (think T4), one could produce up to 6 overloads (because Tuple is limited to 7 generic arguments) of something similar to:
public static class Iterate
{
public static IEnumerable<Tuple<T1, T2, T3>> Over<T1, T2, T3>(IEnumerable<T1> t1s, IEnumerable<T2> t2s, IEnumerable<T3> t3s)
{
using(var it1s = t1s.GetEnumerator())
using(var it2s = t2s.GetEnumerator())
using(var it3s = t3s.GetEnumerator())
{
while(it1s.MoveNext() && it2s.MoveNext() && it3s.MoveNext())
yield return Tuple.Create(it1s.Current, it2s.Current, it3s.Current);
}
}
}
With this Iterate class, iteration becomes very simple:
foreach(var t in Iterate.Over(
new[] { 1, 2, 3 },
new[] { "a", "b", "c" },
new[] { 1f, 2f, 3f }))
{
}
This can be futher generalized (with a total loss of type safety) to:
public static IEnumerable<object[]> Over(params IEnumerable[] enumerables)

Why not good old for loop?
int n = new int[] {
list1.Count,
list2.Count,
list3.Count,
// etc.
}.Min(); // if lists have different number of items
for (int i = 0; i < n; ++i) {
var item1 = list1[i]; // if you want an item
...
}

As far as I get it, the real problem is the unknown number of lists to iterate over. Another issue I see is that there is no guarantee that all the lists will have the same length... correct?
If the number of lists is unknown, tuples won't do it because they will go up to 8... and must be set at compile time...
In that case i would suggest that you, instead of mapping to a tuple, do it to a simple and very old structure: a matrix! The width will be the number of list (known at runtime) and the depth will be the longest list. You can iterate using a simple and well know for, have the compiler optimise memory and allocation... The code will be very readable not only by C# folks but for practically anyone who works with any kind of programming language...

Adding to #AntonGogolev's answer, on his last remark... if you don't care about type-safety and performance (for boxing-unboxing), you could implement an enumerator using object[]:
public static class Iterator
{
public static IEnumerable<object[]> Enumerate(params IEnumerable[] enumerables)
{
var list = new List<object>();
var enumerators = new List<IEnumerator>();
bool end = false;
foreach(var enu in enumerables)
{
enumerators.Add(enu.GetEnumerator());
}
while(!end)
{
list.Clear();
foreach(var enu in enumerators)
{
if(!enu.MoveNext()) { end = true; break; }
list.Add(enu.Current);
}
if(!end) yield return list.ToArray();
}
}
}
Warning: no effort whatsoever has been made to optimize this code and it has been written as it came through the fingers :-)
You can use it like:
var listA = new[] { 1, 2, 3 };
var listB = new[] { "a", "b", "c" };
var listC = new[] { 5f, 6f, 7f };
foreach(var n in Iterator.Enumerate(listA, listB, listC))
{
foreach(var obj in n)
{
Console.Write(obj.ToString() + ", ");
}
Console.WriteLine();
}
Fiddle here: https://dotnetfiddle.net/irTY8M

Related

C# equivalent of Julia's map!() method

I'm trying to find C#'s equivalent of Julia's map!() method, which is of void return type and takes a function, a destination and a collection on which the function acts.
The best thing that I could find is C#'s Enumerable.Select(), which takes the function as the third argument, and the collection as the first argument. However, it returns a new collection instead of modifying the one in the "destination". That resembles Julia's map() more.
There's nothing as standard like this, but you can easily add your own extension method to IEnumerable to add this functionality. For example:
public static void JuliaMap<TFrom, TTo>
(
this IEnumerable<TFrom> source,
IList<TTo> target,
Func<TFrom, TTo> selector
)
{
var next = 0;
foreach(var value in source)
{
var convertedValue = selector(value);
target[next] = convertedValue;
next++;
}
}
How you can say:
var numbers = new[]{1, 2, 3};
var target = new string[3];
numbers.JuliaMap(target, i => (i * 2).ToString());
NOTE: I've left out any error handling. For example, you'll want to make sure that the target list is long enough to take the inserted value.
Everything in LINQ is by design meant to never modify the underlying collection and to always create a new enumeration that is generally then used to instantiate a new collection.
You can write a helper function like this to achieve what you want:
public static void SelectToDestination<TSource, TResult>(
Func<TSource, TResult> selector,
IEnumerable<TSource> source,
IList<TResult> destination)
{
int i = 0;
foreach (var item in source.Select(selector))
{
destination[i] = item;
i++;
}
}
Usage looks like this:
var l1 = new List<int>() { 1, 2, 3, 4 };
var l2 = new List<int>() { 0, 0, 0, 0 };
SelectToDestination(x => x + 2, l1, l2);
foreach(var item in l2)
{
Console.Write(item + " ");
}
Results in: 3 4 5 6
Since we're using IList<T> in the method's signature, the destination can be an array too and it will work fine:
public static void Main(string[] args)
{
var l1 = new List<int>() { 1, 2, 3, 4 };
var l2 = new int[4];
SelectToDestination(x => x + 2, l1, l2);
foreach(var item in l2)
{
Console.Write(item + " ");
}
}
It takes advantage of the fact that until you call something like ToArray() or ToList(), LINQ hasn't yet instantiated a new collection, it's just iterating lazily over elements in the source collection. So just don't call ToArray() or ToList(), iterate over the resulting IEnumerable<TResult> and assign it to destination. Note that there are probably more perf friendly ways of doing this if that's a concern for you.
Just like Julia's map method, this will only work if the destination collection is at least as big.

Compare two list of string in a single iteration using C# - Unsorted list

I wish to implement a Logic for Compare two List in a single iteration using C# (Un-Sorted List).
For Example:
List<string> listA = new List<string>() {"IOS", "Android", "Windows"};
List<string> listB = new List<string>() {"LINUS", "IOS"};
now I need to compare listB with listA, and I need to trace the missing items in listB like "Android", "Windows" without using C# predefined methods.
Note: Iterate each list only once.
Kindly assist me.
This is most likely one of the most optimized answers you are likely to find:
public static List<T> Except<T>(List<T> a, List<T> b)
{
var hash = new HashSet<T>(b);
var results = new List<T>(a.Count);
foreach (var item in a)
{
if (!hash.Contains(item))
{
results.Add(item);
}
}
return results;
}
Rather than the X x Y iterations you get from comparing lists directly, you get X + Y - Y from iterating the comparison list (when converting to a hash table), and X for iterating over the source list (no additional Y since hash table lookups are constant time).
try this
var objectList3 = listA.Where(o => !listB.Contains(o)).ToList();
I don't know if I got it completly right (please correct me if not), but this could be helpful:
//Remove all elements of b from a
foreach (string item in b)
{
a.Remove(item);
}
// check for all elements of a if they exist in b and store them in c if not
public static List<string> Excepts(List<string> a, List<string> b)
{
List<string> c = new List<string>();
foreach (string s1 in a)
{
bool found = false;
foreach (string s2 in b)
{
if (s1 == s2)
{
found = true;
break;
}
}
if (!found)
c.Add(s1);
}
return c;
}

Using LINQ's Zip with a closure that doesn't return a value

Disclaimer: this question is driven by my personal curiosity more than an actual need to accomplish something. So my example is going to be contrived.
Nevertheless I think it's an issue that might very well crop up.
Let's say we are using Zip to iterate over two sequences, invoking a void method that just throws an exception if one item of the couple is found to be different from the other (therefore discarding any return value). The point here is not that the method throws an exception, so much as it returns void.
In other words, we're kind of doing a ForEach over two collections (and by the way, I know what Eric Lippert thinks about ForEach, and fully agree with him and never use it).
Now, Zip wants a Func<TFirst, TSecond, TResult>, so of course passing something equivalent to Action<TFirst, TSecond> won't work.
My question is: is there an idiomatic way that is better than this (i.e. returning a dummy value)?
var collection1 = new List<int>() { ... };
var collection2 = new List<int>() { ... };
collection1.Zip(collection2, (first, second) =>
{
VoidMethodThatThrows(first, second);
return true;
});
Use Zip() to throw the items into an object, then do your foreach however way you choose (do a normal foreach loop please, not the bad ToList/ForEach combo).
var items = collection1.Zip(collection2, (x, y) => new { First = x, Second = y });
foreach (var item in items)
{
VoidMethodThatThrows(item.First, item.Second);
}
As of C# 7.0, improved tuple support and deconstruction makes it far more pleasing to work with.
var items = collection1.Zip(collection2, (x, y) => (x, y));
// or collection1.Zip(collection2, ValueTuple.Create);
foreach (var (first, second) in items)
{
VoidMethodThatThrows(first, second);
}
Furthermore, .NET Core and 5 adds an overload which automatically pairs the values into tuples so you don't have to do that mapping.
var items = collection1.Zip(collection2); // IEnumerable<(Type1, Type2)>
.NET 6 adds a third collection to the mix.
var items = collection1.Zip(collection2, collection3); // IEnumerable<(Type1, Type2, Type3)>
I often need to execute an action on each pair in two collections. The Zip method is not useful in this case.
This extension method ForPair can be used:
public static void ForPair<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second,
Action<TFirst, TSecond> action)
{
using (var enumFirst = first.GetEnumerator())
using (var enumSecond = second.GetEnumerator())
{
while (enumFirst.MoveNext() && enumSecond.MoveNext())
{
action(enumFirst.Current, enumSecond.Current);
}
}
}
So for your example, you could write:
var collection1 = new List<int>() { 1, 2 };
var collection2 = new List<int>() { 3, 4 };
collection1.ForPair(collection2, VoidMethodThatThrows);

What is the best way to modify a list in a 'foreach' loop?

A new feature in C# / .NET 4.0 is that you can change your enumerable in a foreach without getting the exception. See Paul Jackson's blog entry An Interesting Side-Effect of Concurrency: Removing Items from a Collection While Enumerating for information on this change.
What is the best way to do the following?
foreach(var item in Enumerable)
{
foreach(var item2 in item.Enumerable)
{
item.Add(new item2)
}
}
Usually I use an IList as a cache/buffer until the end of the foreach, but is there better way?
The collection used in foreach is immutable. This is very much by design.
As it says on MSDN:
The foreach statement is used to
iterate through the collection to get
the information that you want, but can
not be used to add or remove items
from the source collection to avoid
unpredictable side effects. If you
need to add or remove items from the
source collection, use a for loop.
The post in the link provided by Poko indicates that this is allowed in the new concurrent collections.
Make a copy of the enumeration, using an IEnumerable extension method in this case, and enumerate over it. This would add a copy of every element in every inner enumerable to that enumeration.
foreach(var item in Enumerable)
{
foreach(var item2 in item.Enumerable.ToList())
{
item.Add(item2)
}
}
To illustrate Nippysaurus's answer: If you are going to add the new items to the list and want to process the newly added items too during the same enumeration then you can just use for loop instead of foreach loop, problem solved :)
var list = new List<YourData>();
... populate the list ...
//foreach (var entryToProcess in list)
for (int i = 0; i < list.Count; i++)
{
var entryToProcess = list[i];
var resultOfProcessing = DoStuffToEntry(entryToProcess);
if (... condition ...)
list.Add(new YourData(...));
}
For runnable example:
void Main()
{
var list = new List<int>();
for (int i = 0; i < 10; i++)
list.Add(i);
//foreach (var entry in list)
for (int i = 0; i < list.Count; i++)
{
var entry = list[i];
if (entry % 2 == 0)
list.Add(entry + 1);
Console.Write(entry + ", ");
}
Console.Write(list);
}
Output of last example:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3, 5, 7, 9,
List (15 items)
0
1
2
3
4
5
6
7
8
9
1
3
5
7
9
As mentioned, but with a code sample:
foreach(var item in collection.ToArray())
collection.Add(new Item...);
You should really use for() instead of foreach() in this case.
You can't change the enumerable collection while it is being enumerated, so you will have to make your changes before or after enumerating.
The for loop is a nice alternative, but if your IEnumerable collection does not implement ICollection, it is not possible.
Either:
1) Copy collection first. Enumerate the copied collection and change the original collection during the enumeration. (#tvanfosson)
or
2) Keep a list of changes and commit them after the enumeration.
LINQ is very effective for juggling with collections.
Your types and structure are unclear to me, but I will try to fit your example to the best of my ability.
From your code it appears that, for each item, you are adding to that item everything from its own 'Enumerable' property. This is very simple:
foreach (var item in Enumerable)
{
item = item.AddRange(item.Enumerable));
}
As a more general example, let's say we want to iterate a collection and remove items where a certain condition is true. Avoiding foreach, using LINQ:
myCollection = myCollection.Where(item => item.ShouldBeKept);
Add an item based on each existing item? No problem:
myCollection = myCollection.Concat(myCollection.Select(item => new Item(item.SomeProp)));
Here's how you can do that (quick and dirty solution. If you really need this kind of behavior, you should either reconsider your design or override all IList<T> members and aggregate the source list):
using System;
using System.Collections.Generic;
namespace ConsoleApplication3
{
public class ModifiableList<T> : List<T>
{
private readonly IList<T> pendingAdditions = new List<T>();
private int activeEnumerators = 0;
public ModifiableList(IEnumerable<T> collection) : base(collection)
{
}
public ModifiableList()
{
}
public new void Add(T t)
{
if(activeEnumerators == 0)
base.Add(t);
else
pendingAdditions.Add(t);
}
public new IEnumerator<T> GetEnumerator()
{
++activeEnumerators;
foreach(T t in ((IList<T>)this))
yield return t;
--activeEnumerators;
AddRange(pendingAdditions);
pendingAdditions.Clear();
}
}
class Program
{
static void Main(string[] args)
{
ModifiableList<int> ints = new ModifiableList<int>(new int[] { 2, 4, 6, 8 });
foreach(int i in ints)
ints.Add(i * 2);
foreach(int i in ints)
Console.WriteLine(i * 2);
}
}
}
To add to Timo's answer LINQ can be used like this as well:
items = items.Select(i => {
...
//perform some logic adding / updating.
return i / return new Item();
...
//To remove an item simply have logic to return null.
//Then attach the Where to filter out nulls
return null;
...
}).Where(i => i != null);
The best approach from a performance perspective is probably to use a one or two arrays. Copy the list to an array, do operations on the array, and then build a new list from the array. Accessing an array element is faster than accessing a list item, and conversions between a List<T> and a T[] can use a fast "bulk copy" operation which avoids the overhead associated accessing individual items.
For example, suppose you have a List<string> and wish to have every string in the list which starts with T be followed by an item "Boo", while every string that starts with "U" is dropped entirely. An optimal approach would probably be something like:
int srcPtr,destPtr;
string[] arr;
srcPtr = theList.Count;
arr = new string[srcPtr*2];
theList.CopyTo(arr, theList.Count); // Copy into second half of the array
destPtr = 0;
for (; srcPtr < arr.Length; srcPtr++)
{
string st = arr[srcPtr];
char ch = (st ?? "!")[0]; // Get first character of string, or "!" if empty
if (ch != 'U')
arr[destPtr++] = st;
if (ch == 'T')
arr[destPtr++] = "Boo";
}
if (destPtr > arr.Length/2) // More than half of dest. array is used
{
theList = new List<String>(arr); // Adds extra elements
if (destPtr != arr.Length)
theList.RemoveRange(destPtr, arr.Length-destPtr); // Chop to proper length
}
else
{
Array.Resize(ref arr, destPtr);
theList = new List<String>(arr); // Adds extra elements
}
It would have been helpful if List<T> provided a method to construct a list from a portion of an array, but I'm unaware of any efficient method for doing so. Still, operations on arrays are pretty fast. Of note is the fact that adding and removing items from the list does not require "pushing" around other items; each item gets written directly to its appropriate spot in the array.
I have written one easy step, but because of this performance will be degraded
Here is my code snippet:-
for (int tempReg = 0; tempReg < reg.Matches(lines).Count; tempReg++)
{
foreach (Match match in reg.Matches(lines))
{
var aStringBuilder = new StringBuilder(lines);
aStringBuilder.Insert(startIndex, match.ToString().Replace(",", " ");
lines[k] = aStringBuilder.ToString();
tempReg = 0;
break;
}
}

Is there a built-in method to compare collections?

I would like to compare the contents of a couple of collections in my Equals method. I have a Dictionary and an IList. Is there a built-in method to do this?
Edited:
I want to compare two Dictionaries and two ILists, so I think what equality means is clear - if the two dictionaries contain the same keys mapped to the same values, then they're equal.
Enumerable.SequenceEqual
Determines whether two sequences are equal by comparing their elements by using a specified IEqualityComparer(T).
You can't directly compare the list & the dictionary, but you could compare the list of values from the Dictionary with the list
As others have suggested and have noted, SequenceEqual is order-sensitive. To solve that, you can sort the dictionary by key (which is unique, and thus the sort is always stable) and then use SequenceEqual. The following expression checks if two dictionaries are equal regardless of their internal order:
dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))
EDIT: As pointed out by Jeppe Stig Nielsen, some object have an IComparer<T> that is incompatible with their IEqualityComparer<T>, yielding incorrect results. When using keys with such an object, you must specify a correct IComparer<T> for those keys. For example, with string keys (which exhibit this issue), you must do the following in order to get correct results:
dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
In addition to the mentioned SequenceEqual, which
is true if two lists are of equal length and their corresponding
elements compare equal according to a comparer
(which may be the default comparer, i.e. an overriden Equals())
it is worth mentioning that in .Net4 there is SetEquals on ISet objects,
which
ignores the order of elements and any duplicate elements.
So if you want to have a list of objects, but they don't need to be in a specific order, consider that an ISet (like a HashSet) may be the right choice.
Take a look at the Enumerable.SequenceEqual method
var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);
This is not directly answering your questions, but both the MS' TestTools and NUnit provide
CollectionAssert.AreEquivalent
which does pretty much what you want.
I didn't know about Enumerable.SequenceEqual method (you learn something every day....), but I was going to suggest using an extension method; something like this:
public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
{
if (InternalList.Count != ExternalList.Count)
{
return false;
}
else
{
for (int i = 0; i < InternalList.Count; i++)
{
if (InternalList[i] != ExternalList[i])
return false;
}
}
return true;
}
Interestingly enough, after taking 2 seconds to read about SequenceEqual, it looks like Microsoft has built the function I described for you.
.NET Lacks any powerful tools for comparing collections. I've developed a simple solution you can find at the link below:
http://robertbouillon.com/2010/04/29/comparing-collections-in-net/
This will perform an equality comparison regardless of order:
var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;
This will check to see if items were added / removed:
var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added; //Sally
var inbothlists = diff.Equal; //Bob
This will see what items in the dictionary changed:
var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa
To compare collections you can also use LINQ. Enumerable.Intersect returns all pairs that are equal. You can comparse two dictionaries like this:
(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count
The first comparison is needed because dict2 can contain all the keys from dict1 and more.
You can also use think of variations using Enumerable.Except and Enumerable.Union that lead to similar results. But can be used to determine the exact differences between sets.
How about this example:
static void Main()
{
// Create a dictionary and add several elements to it.
var dict = new Dictionary<string, int>();
dict.Add("cat", 2);
dict.Add("dog", 3);
dict.Add("x", 4);
// Create another dictionary.
var dict2 = new Dictionary<string, int>();
dict2.Add("cat", 2);
dict2.Add("dog", 3);
dict2.Add("x", 4);
// Test for equality.
bool equal = false;
if (dict.Count == dict2.Count) // Require equal count.
{
equal = true;
foreach (var pair in dict)
{
int value;
if (dict2.TryGetValue(pair.Key, out value))
{
// Require value be equal.
if (value != pair.Value)
{
equal = false;
break;
}
}
else
{
// Require key be present.
equal = false;
break;
}
}
}
Console.WriteLine(equal);
}
Courtesy : https://www.dotnetperls.com/dictionary-equals
For ordered collections (List, Array) use SequenceEqual
for HashSet use SetEquals
for Dictionary you can do:
namespace System.Collections.Generic {
public static class ExtensionMethods {
public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
if (object.ReferenceEquals(d1, d2)) return true;
if (d2 is null || d1.Count != d2.Count) return false;
foreach (var (d1key, d1value) in d1) {
if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
if (!d1value.Equals(d2value)) return false;
}
return true;
}
}
}
(A more optimized solution will use sorting but that will require IComparable<TValue>)
No, because the framework doesn't know how to compare the contents of your lists.
Have a look at this:
http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx
public bool CompareStringLists(List<string> list1, List<string> list2)
{
if (list1.Count != list2.Count) return false;
foreach(string item in list1)
{
if (!list2.Contains(item)) return false;
}
return true;
}
There wasn't, isn't and might not be, at least I would believe so. The reason behind is collection equality is probably an user defined behavior.
Elements in collections are not supposed to be in a particular order though they do have an ordering naturally, it's not what the comparing algorithms should rely on. Say you have two collections of:
{1, 2, 3, 4}
{4, 3, 2, 1}
Are they equal or not? You must know but I don't know what's your point of view.
Collections are conceptually unordered by default, until the algorithms provide the sorting rules. The same thing SQL server will bring to your attention is when you trying to do pagination, it requires you to provide sorting rules:
https://learn.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017
Yet another two collections:
{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}
Again, are they equal or not? You tell me ..
Element repeatability of a collection plays its role in different scenarios and some collections like Dictionary<TKey, TValue> don't even allow repeated elements.
I believe these kinds of equality are application defined and the framework therefore did not provide all of the possible implementations.
Well, in general cases Enumerable.SequenceEqual is good enough but it returns false in the following case:
var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false
I read some answers to questions like this(you may google for them) and what I would use, in general:
public static class CollectionExtensions {
public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
if(object.ReferenceEquals(first, second)) {
return true;
}
if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
return Enumerable.SequenceEqual(first, second);
}
if(first is ICollection<T> && second is ICollection<T>) {
if(first.Count()!=second.Count()) {
return false;
}
}
first=first.OrderBy(x => x.GetHashCode());
second=second.OrderBy(x => x.GetHashCode());
return CollectionExtensions.Represents(first, second);
}
}
That means one collection represents the other in their elements including repeated times without taking the original ordering into account. Some notes of the implementation:
GetHashCode() is just for the ordering not for equality; I think it's enough in this case
Count() will not really enumerates the collection and directly fall into the property implementation of ICollection<T>.Count
If the references are equal, it's just Boris
I've made my own compare method. It returns common, missing, and extra values.
private static void Compare<T>(IEnumerable<T> actual, IEnumerable<T> expected, out IList<T> common, out IList<T> missing, out IList<T> extra) {
common = new List<T>();
missing = new List<T>();
extra = new List<T>();
var expected_ = new LinkedList<T>( expected );
foreach (var item in actual) {
if (expected_.Remove( item )) {
common.Add( item );
} else {
extra.Add( item );
}
}
foreach (var item in expected_) {
missing.Add( item );
}
}
Comparing dictionaries' contents:
To compare two Dictionary<K, V> objects, we can assume that the keys are unique for every value, thus if two sets of keys are equal, then the two dictionaries' contents are equal.
Dictionary<K, V> dictionaryA, dictionaryB;
bool areDictionaryContentsEqual = new HashSet<K>(dictionaryA.Keys).SetEquals(dictionaryB.Keys);
Comparing collections' contents:
To compare two ICollection<T> objects, we need to check:
If they are of the same length.
If every T value that appears in the first collection appears an equal number of times in the second.
public static bool AreCollectionContentsEqual<T>(ICollection<T> collectionA, ICollection<T> collectionB)
where T : notnull
{
if (collectionA.Count != collectionB.Count)
{
return false;
}
Dictionary<T, int> countByValueDictionary = new(collectionA.Count);
foreach(T item in collectionA)
{
countByValueDictionary[item] = countByValueDictionary.TryGetValue(item, out int count)
? count + 1
: 1;
}
foreach (T item in collectionB)
{
if (!countByValueDictionary.TryGetValue(item, out int count) || count < 1)
{
return false;
}
countByValueDictionary[item] = count - 1;
}
return true;
}
These solutions should be optimal since their time and memory complexities are O(n), while the solutions that use ordering/sorting have time and memory complexities greater than O(n).

Categories

Resources