Linq OrderByDescending after that ThenByDescending in C# - c#

I have list of integer and I am trying to sort this based on first in descending order and after that I want to sort with in two list based on even and odds but still in descending order like this
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers = new List<int>() { 3, 7, 1, 5, 4, 6, 2 };
var sortedDescendingNumbers = numbers.OrderByDescending(x => x);
var sortedNumbers = sortedDescendingNumbers.ThenByDescending(x => x % 2 == 0);
foreach (var num in sortedNumbers)
{
Console.Write(num + " ");
}
}
}
but this one print the result as 7 6 5 4 3 2 1 and I am expecting output like 7 5 3 1 6 4 2

Your first block of numbers should be the odd ones, so you have to order by divisibility by 2 first and then by descending value.
var sortedOddEvenNumbers = numbers.OrderBy(x => x % 2 == 0);
var sortedNumbers = sortedOddEvenNumbers.ThenByDescending(x => x);

Related

ThenByDescending doesn't change anything in output

I am using following code and I understand that first OrderByDescending sort the list in descending order but when I use ThenByDescending(x => x % 2 == 0) nothing happened on the result list
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers = new List<int>() { 3, 7, 1, 5, 4, 6, 2 };
var sortedNumbers = numbers.OrderByDescending(x => x);
foreach (int i in sortedNumbers )
{
Console.WriteLine(i);
}
var sortedNumbers1 = sortedNumbers.ThenByDescending(x => x % 2 == 0);
foreach (int i in sortedNumbers1 )
{
Console.WriteLine(i);
}
}
}
but If I change my code to following the second sort start to work , or at least it shows the change in result set.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers = new List<int>() { 3, 7, 1, 5, 4, 6, 2 };
var sortedNumbers = numbers.OrderByDescending(x => x % 2 == 0);
foreach (int i in sortedNumbers )
{
Console.WriteLine(i);
}
Console.WriteLine("*-******************");
var sortedNumbers1 = sortedNumbers.ThenByDescending(x => x);
foreach (int i in sortedNumbers1 )
{
Console.WriteLine(i);
}
}
}
The operator used is ThenByDescending, which means it's applied in conjunction with the original operator to order any duplicates produced by the first. Since there are no duplicates, the second condition has no effect.
In SQL the equivalent would be this, and equally ineffectual :
ORDER BY num DESC, IIF(num %2,1,0) DESC
ThenByDescending is applied when there is a "collision" in the ordering i.e. you have elements which have the same order for the previous (Then)OrderBy(Descending) clauses. From the docs:
Performs a subsequent ordering of the elements in a sequence in descending order.
var sortedNumbers = numbers.OrderByDescending(x => x); does not have elements with ambiguous order so ThenByDescending(x => x % 2 == 0) has no effect.
In the second snippet where x => x % 2 == 0 is used for ordering first so you end up with two buckets of elements (4, 6, 2 and 3, 7, 1, 5) so ThenByDescending(x => x) is applied inside each one.

Compare 2 Lists of numbers in LINQ Method Syntax

I have 2 lists of numbers.
public int[] numbersA = { 0, 2, 4 };
public int[] numbersB = { 1, 3, 5 };
I need output like below
Expected Result
0 is less than 1
0 is less than 3
0 is less than 5
2 is less than 3
2 is less than 5
4 is less than 5
How is it possible through LINQ method syntax?
With Method syntax:
var result = numbersA.SelectMany(c => numbersB, (c, o) => new { c, o })
.Where(d => d.c < d.o)
.Select(v=> v.c + "is less than"+ v.o);
At times, verbosity take precedence over brevity as it is clearer and easier to read in most situation, although maybe a bit longer to type.
There is no direct way to achieve what you want as you use Array instead of List (List has ForEach)
But if you want with Arrays I suggest using Array.ForEach.
int[] numbersA = new int[] { 0, 2, 4 };
int[] numbersB = new int[] { 1, 3, 5 };
Array.ForEach(numbersA, x =>
{
Array.ForEach(numbersB, y =>
{
if (x < y)
Console.WriteLine(x + " is less than " + y);
});
Console.WriteLine(Environment.NewLine);
});
Although this question is nothing more than useless business logic, it looks funny to have a try. My solution is List.Foreach rather than Linq, but it is in only one statement.
static void Main(string[] args)
{
int[] numsA = { 0, 2, 4 };
int[] numsB = { 1, 3, 5 };
numsA.ToList().ForEach((a) =>
{
numsB.Where(b => b > a).ToList()
.ForEach((x) =>
{
Console.WriteLine("{0}>{1}", a, x);
});
});
}
Give this a try:
int[] numbersA = { 0, 2, 4 };
int[] numbersB = { 1, 3, 5 };
var result = numbersA.Select(a => numbersB.Where(b => a < b)
.Select(b => a + " is less than " + b))
.SelectMany(arr => arr)
.ToArray();

The union of the intersects of the 2 set combinations of a sequence of sequences

How can I find the set of items that occur in 2 or more sequences in a sequence of sequences?
In other words, I want the distinct values that occur in at least 2 of the passed in sequences.
Note:
This is not the intersect of all sequences but rather, the union of the intersect of all pairs of sequences.
Note 2:
The does not include the pair, or 2 combination, of a sequence with itself. That would be silly.
I have made an attempt myself,
public static IEnumerable<T> UnionOfIntersects<T>(
this IEnumerable<IEnumerable<T>> source)
{
var pairs =
from s1 in source
from s2 in source
select new { s1 , s2 };
var intersects = pairs
.Where(p => p.s1 != p.s2)
.Select(p => p.s1.Intersect(p.s2));
return intersects.SelectMany(i => i).Distinct();
}
but I'm concerned that this might be sub-optimal, I think it includes intersects of pair A, B and pair B, A which seems inefficient. I also think there might be a more efficient way to compound the sets as they are iterated.
I include some example input and output below:
{ { 1, 1, 2, 3, 4, 5, 7 }, { 5, 6, 7 }, { 2, 6, 7, 9 } , { 4 } }
returns
{ 2, 4, 5, 6, 7 }
and
{ { 1, 2, 3} } or { {} } or { }
returns
{ }
I'm looking for the best combination of readability and potential performance.
EDIT
I've performed some initial testing of the current answers, my code is here. Output below.
Original valid:True
DoomerOneLine valid:True
DoomerSqlLike valid:True
Svinja valid:True
Adricadar valid:True
Schmelter valid:True
Original 100000 iterations in 82ms
DoomerOneLine 100000 iterations in 58ms
DoomerSqlLike 100000 iterations in 82ms
Svinja 100000 iterations in 1039ms
Adricadar 100000 iterations in 879ms
Schmelter 100000 iterations in 9ms
At the moment, it looks as if Tim Schmelter's answer performs better by at least an order of magnitude.
// init sequences
var sequences = new int[][]
{
new int[] { 1, 2, 3, 4, 5, 7 },
new int[] { 5, 6, 7 },
new int[] { 2, 6, 7, 9 },
new int[] { 4 }
};
One-line way:
var result = sequences
.SelectMany(e => e.Distinct())
.GroupBy(e => e)
.Where(e => e.Count() > 1)
.Select(e => e.Key);
// result is { 2 4 5 7 6 }
Sql-like way (with ordering):
var result = (
from e in sequences.SelectMany(e => e.Distinct())
group e by e into g
where g.Count() > 1
orderby g.Key
select g.Key);
// result is { 2 4 5 6 7 }
May be fastest code (but not readable), complexity O(N):
var dic = new Dictionary<int, int>();
var subHash = new HashSet<int>();
int length = array.Length;
for (int i = 0; i < length; i++)
{
subHash.Clear();
int subLength = array[i].Length;
for (int j = 0; j < subLength; j++)
{
int n = array[i][j];
if (!subHash.Contains(n))
{
int counter;
if (dic.TryGetValue(n, out counter))
{
// duplicate
dic[n] = counter + 1;
}
else
{
// first occurance
dic[n] = 1;
}
}
else
{
// exclude duplucate in sub array
subHash.Add(n);
}
}
}
This should be very close to optimal - how "readable" it is depends on your taste. In my opinion it is also the most readable solution.
var seenElements = new HashSet<T>();
var repeatedElements = new HashSet<T>();
foreach (var list in source)
{
foreach (var element in list.Distinct())
{
if (seenElements.Contains(element))
{
repeatedElements.Add(element);
}
else
{
seenElements.Add(element);
}
}
}
return repeatedElements;
You can skip already Intesected sequences, this way will be a little faster.
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source)
{
var result = new List<T>();
var sequences = source.ToList();
for (int sequenceIdx = 0; sequenceIdx < sequences.Count(); sequenceIdx++)
{
var sequence = sequences[sequenceIdx];
for (int targetSequenceIdx = sequenceIdx + 1; targetSequenceIdx < sequences.Count; targetSequenceIdx++)
{
var targetSequence = sequences[targetSequenceIdx];
var intersections = sequence.Intersect(targetSequence);
result.AddRange(intersections);
}
}
return result.Distinct();
}
How it works?
Input: {/*0*/ { 1, 2, 3, 4, 5, 7 } ,/*1*/ { 5, 6, 7 },/*2*/ { 2, 6, 7, 9 } , /*3*/{ 4 } }
Step 0: Intersect 0 with 1..3
Step 1: Intersect 1 with 2..3 (0 with 1 already has been intersected)
Step 2: Intersect 2 with 3 (0 with 2 and 1 with 2 already has been intersected)
Return: Distinct elements.
Result: { 2, 4, 5, 6, 7 }
You can test it with the below code
var lists = new List<List<int>>
{
new List<int> {1, 2, 3, 4, 5, 7},
new List<int> {5, 6, 7},
new List<int> {2, 6, 7, 9},
new List<int> {4 }
};
var result = lists.UnionOfIntersects();
You can try this approach, it might be more efficient and also allows to specify the minimum intersection-count and the comparer used:
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source
, int minIntersectionCount
, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
foreach (T item in source.SelectMany(s => s).Distinct(comparer))
{
int containedInHowManySequences = 0;
foreach (IEnumerable<T> seq in source)
{
bool contained = seq.Contains(item, comparer);
if (contained) containedInHowManySequences++;
if (containedInHowManySequences == minIntersectionCount)
{
yield return item;
break;
}
}
}
}
Some explaining words:
It enumerates all unique items in all sequences. Since Distinct is using a set this should be pretty efficient. That can help to speed up in case of many duplicates in all sequences.
The inner loop just looks into every sequence if the unique item is contained. Thefore it uses Enumerable.Contains which stops execution as soon as one item was found(so duplicates are no issue).
If the intersection-count reaches the minum intersection count this item is yielded and the next (unique) item is checked.
That should nail it:
int[][] test = { new int[] { 1, 2, 3, 4, 5, 7 }, new int[] { 5, 6, 7 }, new int[] { 2, 6, 7, 9 }, new int[] { 4 } };
var result = test.SelectMany(a => a.Distinct()).GroupBy(x => x).Where(g => g.Count() > 1).Select(y => y.Key).ToList();
First you make sure, there are no duplicates in each sequence. Then you join all sequences to a single sequence and look for duplicates as e.g. here.

Get a list of array of lists idexes with greates elementrs counts C#

I have an array of lists:
private List<int>[] Graph = new List<int>[n];
For example:
Graph[0] = new List<int>() { 1, 2 };
Graph[1] = new List<int>() { 0 };
Graph[2] = new List<int>() { 0, 1, 3, 4 };
Graph[3] = new List<int>() { 2, 4, 1 };
Graph[4] = new List<int>() { 2, 3 };
Graph[0].Count // give 2
Graph[1].Count // give 1
Graph[2].Count // give 4
Graph[3].Count // give 3
Graph[4].Count // give 2
And I want to get an array (or list) which includes the indexes of the lists sorted by the count of the elements in each list. So for this example it will be:
orderList[0] -> 2 //(because Graph[2].Count give 4)
orderList[1] -> 3 //(because Graph[3].Count give 3)
orderList[2] -> 0 //(because Graph[0].Count give = 2)
orderList[3] -> 4 //(because Graph[4].Count give = 2)
orderList[4] -> 1 //(because Graph[1].Count give = 1)
orderList is an n-elements array.
You can use the select method that incorporates an index to combine the list count with the index
int[] orderList = Graph.Select((list, index) => new { Count = list.Count, Index = index }).OrderByDescending(a => a.Count).Select(a => a.Index).ToArray();
More readable query syntax
int[] orderList = (from pair in Graph.Select((list, index) => new { Count = list.Count, Index = index })
orderby pair.Count descending
select pair.Index).ToArray();
All you need is:
Graph = Graph.OrderByDescending(x => x.Count).ToArray();
You can use LINQ:
List<int>[] orderedGraph = graph.OrderByDescending(x => x.Count).ToArray();
This will order array descending using list "count" property.
Remember to add using:
using System.Linq;

Joining two lists together

If I have two lists of type string (or any other type), what is a quick way of joining the two lists?
The order should stay the same. Duplicates should be removed (though every item in both links are unique). I didn't find much on this when googling and didn't want to implement any .NET interfaces for speed of delivery.
You could try:
List<string> a = new List<string>();
List<string> b = new List<string>();
a.AddRange(b);
MSDN page for AddRange
This preserves the order of the lists, but it doesn't remove any duplicates (which Union would do).
This does change list a. If you wanted to preserve the original lists then you should use Concat (as pointed out in the other answers):
var newList = a.Concat(b);
This returns an IEnumerable as long as a is not null.
The way with the least space overhead is to use the Concat extension method.
var combined = list1.Concat(list2);
It creates an instance of IEnumerable<T> which will enumerate the elements of list1 and list2 in that order.
The Union method might address your needs. You didn't specify whether order or duplicates was important.
Take two IEnumerables and perform a union as seen here:
int[] ints1 = { 5, 3, 9, 7, 5, 9, 3, 7 };
int[] ints2 = { 8, 3, 6, 4, 4, 9, 1, 0 };
IEnumerable<int> union = ints1.Union(ints2);
// yields { 5, 3, 9, 7, 8, 6, 4, 1, 0 }
Something like this:
firstList.AddRange (secondList);
Or, you can use the 'Union' extension method that is defined in System.Linq.
With 'Union', you can also specify a comparer, which can be used to specify whether an item should be unioned or not.
Like this:
List<int> one = new List<int> { 1, 2, 3, 4, 5 };
List<int> second=new List<int> { 1, 2, 5, 6 };
var result = one.Union (second, new EqComparer ());
foreach( int x in result )
{
Console.WriteLine (x);
}
Console.ReadLine ();
#region IEqualityComparer<int> Members
public class EqComparer : IEqualityComparer<int>
{
public bool Equals( int x, int y )
{
return x == y;
}
public int GetHashCode( int obj )
{
return obj.GetHashCode ();
}
}
#endregion
targetList = list1.Concat(list2).ToList();
It's working fine I think so. As previously said, Concat returns a new sequence and while converting the result to List, it does the job perfectly. Implicit conversions may fail sometimes when using the AddRange method.
If some item(s) exist in both lists you may use
var all = list1.Concat(list2).Concat(list3) ... Concat(listN).Distinct().ToList();
As long as they are of the same type, it's very simple with AddRange:
list2.AddRange(list1);
var bigList = new List<int> { 1, 2, 3 }
.Concat(new List<int> { 4, 5, 6 })
.ToList(); /// yields { 1, 2, 3, 4, 5, 6 }
The AddRange method
aList.AddRange( anotherList );
List<string> list1 = new List<string>();
list1.Add("dot");
list1.Add("net");
List<string> list2 = new List<string>();
list2.Add("pearls");
list2.Add("!");
var result = list1.Concat(list2);
one way: List.AddRange() depending on the types?
One way, I haven't seen mentioned that can be a bit more robust, particularly if you wanted to alter each element in some way (e.g. you wanted to .Trim() all of the elements.
List<string> a = new List<string>();
List<string> b = new List<string>();
// ...
b.ForEach(x=>a.Add(x.Trim()));
See this link
public class ProductA
{
public string Name { get; set; }
public int Code { get; set; }
}
public class ProductComparer : IEqualityComparer<ProductA>
{
public bool Equals(ProductA x, ProductA y)
{
//Check whether the objects are the same object.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether the products' properties are equal.
return x != null && y != null && x.Code.Equals(y.Code) && x.Name.Equals(y.Name);
}
public int GetHashCode(ProductA obj)
{
//Get hash code for the Name field if it is not null.
int hashProductName = obj.Name == null ? 0 : obj.Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = obj.Code.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
ProductA[] store1 = { new ProductA { Name = "apple", Code = 9 },
new ProductA { Name = "orange", Code = 4 } };
ProductA[] store2 = { new ProductA { Name = "apple", Code = 9 },
new ProductA { Name = "lemon", Code = 12 } };
//Get the products from the both arrays
//excluding duplicates.
IEnumerable<ProductA> union =
store1.Union(store2);
foreach (var product in union)
Console.WriteLine(product.Name + " " + product.Code);
/*
This code produces the following output:
apple 9
orange 4
lemon 12
*/
The two options I use are:
list1.AddRange(list2);
or
list1.Concat(list2);
However I noticed as I used that when using the AddRange method with a recursive function, that calls itself very often I got an SystemOutOfMemoryException because the maximum number of dimensions was reached.
(Message Google Translated)
The array dimensions exceeded the supported range.
Using Concat solved that issue.
I just wanted to test how Union works with the default comparer on overlapping collections of reference type objects.
My object is:
class MyInt
{
public int val;
public override string ToString()
{
return val.ToString();
}
}
My test code is:
MyInt[] myInts1 = new MyInt[10];
MyInt[] myInts2 = new MyInt[10];
int overlapFrom = 4;
Console.WriteLine("overlapFrom: {0}", overlapFrom);
Action<IEnumerable<MyInt>, string> printMyInts = (myInts, myIntsName) => Console.WriteLine("{2} ({0}): {1}", myInts.Count(), string.Join(" ", myInts), myIntsName);
for (int i = 0; i < myInts1.Length; i++)
myInts1[i] = new MyInt { val = i };
printMyInts(myInts1, nameof(myInts1));
int j = 0;
for (; j + overlapFrom < myInts1.Length; j++)
myInts2[j] = myInts1[j + overlapFrom];
for (; j < myInts2.Length; j++)
myInts2[j] = new MyInt { val = j + overlapFrom };
printMyInts(myInts2, nameof(myInts2));
IEnumerable<MyInt> myUnion = myInts1.Union(myInts2);
printMyInts(myUnion, nameof(myUnion));
for (int i = 0; i < myInts2.Length; i++)
myInts2[i].val += 10;
printMyInts(myInts2, nameof(myInts2));
printMyInts(myUnion, nameof(myUnion));
for (int i = 0; i < myInts1.Length; i++)
myInts1[i].val = i;
printMyInts(myInts1, nameof(myInts1));
printMyInts(myUnion, nameof(myUnion));
The output is:
overlapFrom: 4
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myInts2 (10): 4 5 6 7 8 9 10 11 12 13
myUnion (14): 0 1 2 3 4 5 6 7 8 9 10 11 12 13
myInts2 (10): 14 15 16 17 18 19 20 21 22 23
myUnion (14): 0 1 2 3 14 15 16 17 18 19 20 21 22 23
myInts1 (10): 0 1 2 3 4 5 6 7 8 9
myUnion (14): 0 1 2 3 4 5 6 7 8 9 20 21 22 23
So, everything works fine.

Categories