Equality between two IEnumerable<IEnumerable<int>> objects - c#

I don't think SequenceEqual is working between the two because the "middle" elements (IEnumerable<int>) aren't using SequenceEqual.
oneThingy.SequenceEqual(twoThingy)
Short of using String.Join on the middle elements, is there a way to get equality?

SequenceEqual tests using Equals; to use SequenceEquals you'll need to implement it yourself. Try using the Zip operator with sequence equals.
// example
var first = Enumerable.Range(1, 10).Select(i => Enumerable.Range(1, i));
var second = Enumerable.Range(1, 10).Select(i => Enumerable.Range(1, i));
bool nestedSequencesEqual =
// test if each sequence index is equal
first.Zip(second, (f, s) => f.SequenceEqual(s))
// ensure all like sequences are equal
.All(b => b);
// returns true

+1 for #BleuM937 answer.
As an another approach you can use the SequenceEqual overloads with equality comparer:
IEnumerable<IEnumerable<int>> one = new IEnumerable<int>[] { new int[] { 1 }, new int[] { 1, 2, 3 } };
IEnumerable<IEnumerable<int>> two = new IEnumerable<int>[] { new int[] { 1 }, new int[] { 1, 2, 3 } };
bool nestedSequencesEqual = one.SequenceEqual(two, new SequencesComparer<int>());
class SequencesComparer<T> : IEqualityComparer<IEnumerable<T>> {
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) {
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj) {
return obj.GetHashCode();
}
}

The following code works for me...
public class IntList : List<int>, IEquatable<IntList>
{
public bool Equals(IntList other)
{
return this.SequenceEqual(other);
}
}
void Main()
{
List<IntList> list1 = new List<IntList>(2);
List<IntList> list2 = new List<IntList>(2);
var list11 = new IntList() {1, 2, 3};
list1.Add(list11);
var list21 = new IntList() {1, 2, 3};
list2.Add(list21);
var result = list1.SequenceEqual(list2);
Console.WriteLine(result);
}
Reference: http://msdn.microsoft.com/en-us/library/bb348567(v=vs.100).aspx

Related

C# How to sort a jagged array by index

I want to sort a jagged array by following condition:
if array[i][0] != array[j][0], compare by array[i][0] - array[j][0]
if array[i][0] != array[j][0], compare by i - j
i, j is index of array
Here is an example:
int[][] array = new int[3][]
{
new int[2] {1, 2},
new int[2] {0, 1},
new int[2] {1, 1}
};
sorted result
[[0, 1], [1, 2], [1, 1]]
i try to use IComparer but don't know how to implement or any other method to solve this problem.
Thanks
=====================================================================
Problem solved but got different result between Array.Sort() and Linq.
public void TestSort()
{
int[][] array = new int[6][]
{
new int[4]{2,6,9,4},
new int[4]{4,8,7,5},
new int[4]{4,6,7,6},
new int[4]{2,3,3,7},
new int[4]{9,3,6,8},
new int[4]{2,8,8,4}
};
// Use Array.Sort()
JaggedArrayComparer cp = new JaggedArrayComparer();
cp.array = array;
Array.Sort(array, cp);
Console.WriteLine("Sort by Array.Sort() =================");
foreach (var e in array)
{
Console.WriteLine($"{e[0]} {e[1]} {e[2]} {e[3]}");
}
// Use Linq
// array = array.OrderBy(a => a[0]).ThenBy(a => Array.IndexOf(array, a)).ToArray();
// Console.WriteLine("Sort by Linq =================");
// foreach (var e in array)
// {
// Console.WriteLine($"{e[0]} {e[1]} {e[2]} {e[3]}");
// }
}
class JaggedArrayComparer : IComparer
{
public int[][] array;
public int Compare(object x, object y)
{
int[] xArray = (int[])x;
int[] yArray = (int[])y;
if (xArray[0] != yArray[0])
{
return xArray[0] - yArray[0];
}
else
{
return Array.IndexOf(array, xArray) - Array.IndexOf(array, yArray);
}
}
}
Output:
Expect output is same as Linq Output. What is the reason
I would use the Sort method and provide it with a comparer function as you like
something like this
Comparison<int[]> MyCoolComparison(List<int[]> intsList)
{
return (x, y) => (x[0] != y[0])
? x[0].CompareTo(y[0]
: intsList.IndexOf(x) - intsList.IndexOf(y);
}
and then the usage is simple:
array.Sort(MyCoolComparison(array));
you can of course create a comparison class implementing the IComparer interface
and in the compare method implement the same logic
and use it like this:
class MyComparerClass: IComparer
{
public int Compare(object x, object y)
{
// logic
}
}
Array.Sort(array, new MyComparerClass());

a Method to find Common integers between 2 arrays

I need to write a method to find the commons between 2 arrays in C# but the thing is I can't convert my python logic from the past to C#
it used to be like this in python:
def commonfinder(list1, list2):
commonlist = []
for x in list1:
for y in list2:
if x==y:
commonlist.append(x)
return commonlist
but when I tried to convert it to C#:
public int [] Commons(int[] ar1, int[] ar2)
{
int commoncount;
int[] Commonslist = new int[commoncount];
foreach (int x in ar1)
{
foreach (int y in ar2)
{
if (x == y)
{
commoncount++;
// here I should add x to Commonlist
}
}
}
return Commonslist;
}
I couldn't find any method or functions that would append x to my Commonlist
and ofc I got a lot of errors I couldn't solve
can I get a tip?
Your original algorithm has O(n * m) time complexity, which can be too long:
imagine that you have lists of 1 million items each (1 trillion compares to perform). You can implement a better code with O(n + m) complexity only:
Code: (let's generalize the problem)
using System.Linq;
...
public static T[] CommonFinder<T>(IEnumerable<T> left,
IEnumerable<T> right,
IEqualityComparer<T> comparer = null) {
if (null == left || null == right)
return new T[0]; // Or throw ArgumentNullException exception
comparer = comparer ?? EqualityComparer<T>.Default;
Dictionary<T, int> dict = right
.GroupBy(item => item)
.ToDictionary(group => group.Key, group => group.Count());
List<T> result = new List<T>();
foreach (T item in left)
if (dict.TryGetValue(item, out int count)) {
result.Add(item);
if (count <= 1)
dict.Remove(item);
else
dict[item] = count - 1;
}
return result.ToArray();
}
Demo:
int[] left = new int[] { 1, 2, 3, 4, 5 };
int[] right = new int[] { 0, 3, 2, 6, 9};
var common = CommonFinder(left, right);
Console.WriteLine(string.Join(", ", common));
Outcome:
2, 3
Note: What I understood is you want a method that takes 2 int arrays and yields 1 int array as the output with the unique intersecting values.
You can use HashSet to speed up to insert and lookup time (amortized O(1)). The running time is O(Max(n,m)) due to us having to go through both the entire arrays (separately). In terms of memory, O(Min(n,m)) because we select the smaller array at the beginning to populate the set and for the rest of the logic naturally won't have more elements than the smaller array because it is the intersect.
The Main method shows you how to utilize the method. CommonIntegers has the logic which you seek.
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestCode.StackOverflow
{
public class So66935672
{
public static void Main(string[] args)
{
int[] intArray1 = new int[] { 9, 9, 1, 3, 5, 6, 10, 9 };
int[] intArray2 = new int[] { 19, 17, 16, 5, 1, 6 };
Console.Write(
CommonIntegers(intArray1, intArray2)
.Select(i => $"{i}, ")
.Aggregate(string.Empty, string.Concat));
}
private static int[] CommonIntegers(int[] intArray1, int[] intArray2)
{
if (intArray1 == null || intArray1.Length == 0
|| intArray2 == null || intArray2.Length == 0)
{
return Array.Empty<int>();
}
var primaryArraySet = new HashSet<int>(); // Contains the unique values from the shorter array
var intersectSet = new HashSet<int>(); // Contains unique values found in both arrays
int[] secondarySet;
// Fill primary set
if (intArray1.Length > intArray2.Length)
{
foreach (var i in intArray2)
primaryArraySet.Add(i);
secondarySet = intArray1;
}
else
{
foreach (var i in intArray1)
primaryArraySet.Add(i);
secondarySet = intArray2;
}
// Fill intersect array
foreach (var i in secondarySet)
if (primaryArraySet.Contains(i))
intersectSet.Add(i);
return intersectSet.ToArray();
}
}
}
You can try this one:
static List<int> CommonFinder(List<int> list1, List<int> list2)
{
List<int> commonList = new List<int>();
foreach (int x in list1)
foreach (int y in list2)
if (x == y)
commonList.Add(x);
return commonList;
}
static void Main()
{
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 2, 3, 4};
var common = CommonFinder(list1, list2);
Console.WriteLine(string.Join(", ", common));
}

Join 2 lists have different length by in LINQ

How can I join 2 lists of different lengths. it should join with the sequence.
Eg.
{1,2,3,4} with {5,6,7}
I need to get result like below.
{{1,5}, {2,6}, {3,7}, {4,null}}
I tried this.
var qry = a.Select((i, index) => new {i, j = b[index]});
But its throwing error since the lists are having different lengths.
Please help me to get the solution.
This should work:
var a = new int?[] { 1, 2, 3, 4 };
var b = new int?[] { 5, 6, 7 };
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] {a.ElementAtOrDefault(n), b.ElementAtOrDefault(n)});
Do note the ? in the array declarations. That is necessary in order to have null values in the resulting list. Omitting the ? causes the result to have 0 instead of null.
If you can't or don't want to declare the arrays as int?, then you'll have to do the cast in the Select like so:
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] { a.Select(i => (int?)i).ElementAtOrDefault(n), b.Select(i => (int?)i).ElementAtOrDefault(n) });
This second bit of code will work correctly with regular int arrays or Lists.
The ugly but working version is the following:
a.Cast<int?>().Concat(Enumerable.Repeat<int?>(null, Math.Max(b.Count() - a.Count(), 0)))
.Zip(b.Cast<int?>()
.Concat(Enumerable.Repeat<int?>(null, Math.Max(a.Count() - b.Count(), 0))),
(x, y) => new { x, y });
Its drawback it double evaluation of a collection (the first one is by calling .Count()).
So it is better just to write an extension
static IEnumerable<TResult> ZipNull<T1, T2, TResult>(this IEnumerable<T1> a, IEnumerable<T2> b, Func<T1?, T2?, TResult> func)
where T1 : struct
where T2 : struct
{
using (var it1 = a.GetEnumerator())
using (var it2 = b.GetEnumerator())
{
while (true)
{
if (it1.MoveNext())
{
if (it2.MoveNext())
{
yield return func(it1.Current, it2.Current);
}
else
{
yield return func(it1.Current, null);
}
}
else
{
if (it2.MoveNext())
{
yield return func(null, it2.Current);
}
else
{
break;
}
}
}
}
}
and use it as
a.ZipNull(b, (x, y) => new { x, y });
What you have is effectively a Zip, but where it zips to the end of the longer, rather than the shorter, of the two sequences. You can write such a Zip method, with something that looks a bit similar to the actual Zip implementation:
public static IEnumerable<TResult> ZipAll<TSource, TSecond, TResult>(this IEnumerable<TSource> source,
IEnumerable<TSecond> other,
Func<TSource, TSecond, TResult> projection)
{
using (var firstIterator = source.GetEnumerator())
using (var secondIterator = other.GetEnumerator())
{
while (true)
{
bool hasFirst = firstIterator.MoveNext();
bool hasSecond = secondIterator.MoveNext();
TSource first = hasFirst ? firstIterator.Current : default(TSource);
TSecond second = hasSecond ? secondIterator.Current : default(TSecond);
if (hasFirst || hasSecond)
yield return projection(first, second);
else
yield break;
}
}
}
With that you can write:
a.ZipAll(b, (i, j) => new { i, j });
You could make the code a bit shorter by requiring the inputs to be lists, but the code wouldn't be any faster as lists, just less typing, and it's not like it's that much extra work to support any sequence, so I'd say it's worth the added few lines of code.
Simply loop through the lists and construct new, let's say Dictionary<int?, int?> out of each list element:
var theFirstList = new List<int?> { 1, 2, 3, 4 };
var theSecondList = new List<int?> { 5, 6, 7 };
var el = new Dictionary<int?, int?>();
var length = Math.Max(theFirstList.Count, theSecondList.Count);
for (int i = 0; i < length; i++)
{
el.Add(theFirstList.ElementAtOrDefault(i), theSecondList.ElementAtOrDefault(i));
}
var x = new[] { 1, 2, 3, 4 }.ToList();
var y = new[] { 5, 6, 7 }.ToList();
var arrayLists = new[] {x, y}.OrderBy(t => t.Count).ToList();
var result = arrayLists
.Last()
.Select((item, i) => new[] { x[i], i < arrayLists.First().Count ? y[i] : (int?)null })
.ToList();
this should work for any IEnumerable

Combine entries from two lists by position using LINQ

Say I have two lists with following entries
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
I want to create another List c where its entries are items inserted by position from two lists. So List c would contain the following entries:
List<int> c = {1, 6, 2, 20, 5, 3, 10}
Is there a way to do it in .NET using LINQ? I was looking at .Zip() LINQ extension, but wasn't sure how to use it in this case.
Thanks in advance!
To do it using LINQ, you can use this piece of LINQPad example code:
void Main()
{
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
var result = Enumerable.Zip(a, b, (aElement, bElement) => new[] { aElement, bElement })
.SelectMany(ab => ab)
.Concat(a.Skip(Math.Min(a.Count, b.Count)))
.Concat(b.Skip(Math.Min(a.Count, b.Count)));
result.Dump();
}
Output:
This will:
Zip the two lists together (which will stop when either runs out of elements)
Producing an array containing the two elements (one from a, another from b)
Using SelectMany to "flatten" this out to one sequence of values
Concatenate in the remainder from either list (only one or neither of the two calls to Concat should add any elements)
Now, having said that, personally I would've used this:
public static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator())
{
bool more1 = enumerator1.MoveNext();
bool more2 = enumerator2.MoveNext();
while (more1 && more2)
{
yield return enumerator1.Current;
yield return enumerator2.Current;
more1 = enumerator1.MoveNext();
more2 = enumerator2.MoveNext();
}
while (more1)
{
yield return enumerator1.Current;
more1 = enumerator1.MoveNext();
}
while (more2)
{
yield return enumerator2.Current;
more2 = enumerator2.MoveNext();
}
}
}
Reasons:
It doesn't enumerate a nor b more than once
I'm skeptical about the performance of Skip
It can work with any IEnumerable<T> and not just List<T>
I'd create an extension method to do it.
public static List<T> MergeAll<T>(this List<T> first, List<T> second)
{
int maxCount = (first.Count > second. Count) ? first.Count : second.Count;
var ret = new List<T>();
for (int i = 0; i < maxCount; i++)
{
if (first.Count < maxCount)
ret.Add(first[i]);
if (second.Count < maxCount)
ret.Add(second[i]);
}
return ret;
}
This would iterate through both lists once. If one list is bigger than the other it will continue to add until it's done.
You could try this code:
List<int> c = a.Select((i, index) => new Tuple<int, int>(i, index * 2))
.Union(b.Select((i, index) => new Tuple<int, int>(i, index * 2 + 1)))
.OrderBy(t => t.Second)
.Select(t => t.First).ToList();
It makes a union of two collections and then sorts that union using index. Elements from the first collection have even indices, from the second - odd ones.
Just wrote a little extension for this:
public static class MyEnumerable
{
public static IEnumerable<T> Smash<T>(this IEnumerable<T> one, IEnumerable<T> two)
{
using (IEnumerator<T> enumeratorOne = one.GetEnumerator(),
enumeratorTwo = two.GetEnumerator())
{
bool twoFinished = false;
while (enumeratorOne.MoveNext())
{
yield return enumeratorOne.Current;
if (!twoFinished && enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
if (!twoFinished)
{
while (enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
}
}
}
Usage:
var a = new List<int> { 1, 2, 5, 10 };
var b = new List<int> { 6, 20, 3 };
var c = a.Smash(b); // 1, 6, 2, 20, 5, 3, 10
var d = b.Smash(a); // 6, 1, 20, 2, 3, 5, 10
This will work for any IEnumerable so you can also do:
var a = new List<string> { "the", "brown", "jumped", "the", "lazy", "dog" };
var b = new List<string> { "quick", "dog", "over" };
var c = a.Smash(b); // the, quick, brown, fox, jumped, over, the, lazy, dog
You could use Concat and an anonymous type which you order by the index:
List<int> c = a
.Select((val, index) => new { val, index })
.Concat(b.Select((val, index) => new { val, index }))
.OrderBy(x => x.index)
.Select(x => x.val)
.ToList();
However, since that's not really elegant and also less efficient than:
c = new List<int>(a.Count + b.Count);
int max = Math.Max(a.Count, b.Count);
int aMax = a.Count;
int bMax = b.Count;
for (int i = 0; i < max; i++)
{
if(i < aMax)
c.Add(a[i]);
if(i < bMax)
c.Add(b[i]);
}
I wouldn't use LINQ at all.
Sorry for adding a third extension method inspired by the other two, but I like it shorter:
static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator()) {
bool more1 = true, more2 = true;
do {
if (more1 && (more1 = enumerator1.MoveNext()))
yield return enumerator1.Current;
if (more2 && (more2 = enumerator2.MoveNext()))
yield return enumerator2.Current;
} while (more1 || more2);
}
}

how to check if the list with the same values exists

In these following code segment::
static void Main(string[] args)
{
List<List<int>> bigList = new List<List<int>> { };
bigList.Add(new List<int> { 1, 2 });
bigList.Add(new List<int> { 2, 3 });
bigList.Add(new List<int> { 3, 4 });
List<int> subList = new List<int> { 1, 2 };
Console.WriteLine(bigList.Contains(subList));
}
the output is:: 'False'.
then what is the method to check this. i mean how will the output become 'True'
If you don't care about duplicate entries in the lists you can use:
bigList.Any(b => new HashSet<int>(b).SetEquals(subList))
If you want both lists to contain exactly the same elements you can use this:
bigList.Any(b => b.OrderBy(x => x).SequenceEqual(subList.OrderBy(x => x)))
If you want both lists to have the same elements in the same order you can use this:
bigList.Any(x => x.SequenceEqual(subList))
If the order doesn't matter you can use Any+All:
bool anyContains = bigList
.Any(l => bigList.Count == l.Count && l.All(i => subList.Contains(i)));
Otherwise you can use Any + SequenceEqual
bool anySequencequals = bigList.Any(l => l.SequenceEqual(subList));
Use the All linq statement
var result = bigList.Where(x => x.All(y => subList.Contains(y)));
You can use SequenceEqual method to check with Any:
bigList.Any(x => x.SequenceEqual(subList))
The reason that your code returns "false" is because you are testing if bigList contains subList. Which it does not! BigList contains a list that looks the same as subList but isn't THE subList.
Try this
bigList.Add(subList);
Complete Code
List<List<int>> bigList = new List<List<int>> { };
List<int> subList = new List<int> { 1, 2 };
bigList.Add(subList); //<<<<<<<<<< Here goes Now bigList contains subList
bigList.Add(new List<int> { 2, 3 });
bigList.Add(new List<int> { 3, 4 });
Console.WriteLine(bigList.Contains(subList));// true
Try using SequenceEqual and Any:
bigList.Any(c => c.SequenceEqual(subList));
Or, if you want to use the other way, with Contains, you'll need to make a custom EqualityComparer:
public class CollectionEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public GetHashCode(IEnumerable<T> obj)
{
unchecked
{
return obj.Select(x => x.GetHashCode())
.Aggregate(17, (a, b) => a * 31 * b);
}
}
}
And then just use Contains like this:
bigList.Contains(sublist, new CollectionEqualityComparer<int>());

Categories

Resources