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());
Related
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));
}
This question already has answers here:
c# Array.FindAllIndexOf which FindAll IndexOf
(10 answers)
Closed 8 years ago.
How to find all positions of a value in array
class Program
{
static void Main(string[] args)
{
int start = 0;
int[] numbers = new int[7] { 2,1,2,1,5,6,5};
}
Something like that:
int[] numbers = new [] { 2, 1, 2, 1, 5, 6, 5 };
int toFind = 5;
// all indexes of "5" {4, 6}
int[] indexes = numbers
.Select((v, i) => new {
value = v,
index = i
})
.Where(pair => pair.value == toFind)
.Select(pair => pair.index)
.ToArray();
List<int> indexes = new List<int>();
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == yourNumber)
indexes.Add(i);
}
Useage is: Array.indexOf(T,value)
please refere to the msdn below.
http://msdn.microsoft.com/en-us/library/system.array.indexof(v=vs.110).aspx
You can make a really simple extension method for sequences to do this:
public static class SequenceExt
{
public static IEnumerable<int> IndicesOfAllElementsEqualTo<T>
(
this IEnumerable<T> sequence,
T target
) where T: IEquatable<T>
{
int index = 0;
foreach (var item in sequence)
{
if (item.Equals(target))
yield return index;
++index;
}
}
}
The extension method works with List<>, arrays, IEnumerable<T> and other collections.
Then your code would look something like this:
var numbers = new [] { 2, 1, 2, 1, 5, 6, 5 };
var indices = numbers.IndicesOfAllElementsEqualTo(5); // Use extension method.
// Make indices into an array if you want, like so
// (not really necessary for this sample code):
var indexArray = indices.ToArray();
// This prints "4, 6":
Console.WriteLine(string.Join(", ", indexArray));
Linq could help
var indexes = numbers
.Select((x, idx) => new { x, idx })
.Where(c => c.x == number)
.Select(c => c.idx);
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);
}
}
I have a function that is simply meant to print out a dictionary of frequent item sets in an easy-to-understand fashion. The goal is to order first by the size of the dictionary key and then by the lexicographical order of a list of numbers. The issue arises in the ThenBy statement as the commented out "hello" will get printed indefinitely. If I change the ThenBy to not use the comparer and simply use another int or string value, it works fine, so I'm clearly doing something wrong.
public static void printItemSets(Dictionary<List<int>, int> freqItemSet)
{
List<KeyValuePair<List<int>, int>> printList = freqItemSet.ToList();
printList = printList.OrderBy(x => x.Key.Count)
.ThenBy(x => x.Key, new ListComparer())
.ToList();
}
The code for the ListComparer is as follows:
public class ListComparer: IEqualityComparer<List<int>>, IComparer<List<int>>
{
public int Compare(List<int> a, List<int> b)
{
int larger = a.Count > b.Count ? 1: -1;
for (int i = 0; i < a.Count && i < b.Count; i++)
{
if (a[i] < b[i])
{
return -1;
}
else if (a[i] > b[i])
{
return 1;
}
else { }
}
return larger;
}
}
VERY simple test case:
int[] a = {1, 3, 5};
int[] b = { 2, 3, 5 };
int[] c = { 1, 2, 3, 5 };
int[] d = { 2, 5 };
int[] e = { 1, 3, 4 };
List<int> aL = a.ToList<int>();
List<int> bL = b.ToList<int>();
List<int> cL = c.ToList<int>();
List<int> dL = d.ToList<int>();
List<int> eL = e.ToList<int>();
Dictionary<List<int>, int> test = new Dictionary<List<int>, int>(new ListComparer());
test.Add(aL, 1);
test.Add(bL, 1);
test.Add(cL, 1);
test.Add(dL, 1);
test.Add(eL, 1);
The issue is that ListComparer is not checking if the arrays are the same. The same array is being passed in twice for both x and y. Checking if x and y are equal will resolve your issue.
Your comparer doesn't handle equal items. If the items are equal the order of the two items is what determines which is considered "larger". The comparer is thus not "reflexive". Being reflexive is a property sorting algorithms rely on.
The first line should be var larger = a.Count.CompareTo(b.Count); instead, so that truly equal lists will return 0 rather than either -1 or 1.
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