ThenByDescending doesn't change anything in output - c#

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.

Related

Linq OrderByDescending after that ThenByDescending in 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);

How the get the mean, median and stdev from list

Is it possible to get the mean, median and stdev result from the list of list.
Here is my initial code that needs to compute:
var myList = new List<List<double>>();
myList.Add(new List<double> { 1, 3, 6, 8});
myList.Add(new List<double> { 1, 2, 3, 4});
myList.Add(new List<double> { 1, 4, 8, 12});
And expected result is to get the mean, median, and stdev of first and last index only:
Mean: 1, 8
Median: 1, 8
Stdev: 0, 3.265986324
I tried to loop the list to get the average but not sure if this is the best way:
foreach(var i in myList )
{
Console.WriteLine(i[0].Average());
}
Any suggestion/comments TIA
This will get you started by showing how to calculate the Average. Note the use of First or Last to get the first / last entry from each of the sub-lists.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Program
{
static void Main(string[] args)
{
var myList = new List<List<double>>();
myList.Add(new List<double> { 1, 3, 6, 8 });
myList.Add(new List<double> { 1, 2, 3, 4 });
myList.Add(new List<double> { 1, 4, 8, 12 });
var averageFirst= myList.Select(z => z.First()).Average();
var averageLast = myList.Select(z => z.Last()).Average();
Console.WriteLine(averageFirst);
Console.WriteLine(averageLast);
Console.ReadLine();
}
}
}
I'll join forces with #mjwills (first and last)
Which is to say,
var listOfFirstValues = myList.Select(z => z.First()).ToList();
var listOfLastValues = myList.Select(z => z.Last()).ToList();
Mean
var mean = myList.Average();
Median The middle of a sorted list of numbers
public double Median(List<double> numbers)
{
if (numbers.Count == 0)
return 0;
numbers = numbers.OrderBy(n=>n).ToList();
var halfIndex = numbers.Count()/2;
if (numbers.Count() % 2 == 0)
return (numbers[halfIndex] + numbers[halfIndex - 1]) / 2.0;
return numbers[halfIndex];
}
Standard Deviation a quantity expressing by how much the members of a group differ from the mean value for the group
private double CalculateStdDev(List<double> values)
{
if (values.Count == 0)
return 0;
var avg = values.Average();
var sum = values.Sum(d => Math.Pow(d - avg, 2));
return Math.Sqrt(sum / (values.Count()-1));
}
Note : Totally untested, and lacking any sanity checks

sort a List<int> in c# ignoring the sign

I have a list as below
var initial = new List { 2, 5, 6,-1, -3, -4 };
Expected output is {-1,2,-3,-4,5,6} =>(sort the given list by ignoring the -ve sign but not removing them in the result.)
My attempt to solve this is
var initial = new List<string> { 2, 5, 6,-1, -3, -4 };
var dummyList = initial.FindAll(x => x < 0);
initial .RemoveAll(x => dummyList.Contains(x));
foreach (var e in dummyList)
{
initial .Add(e * -1);
}
initial.Sort();
foreach (var e in dummyList)
{
var index = initial.FindIndex(x=>x==(e*-1));
initial.RemoveAt(index);
initial.Insert(index, e);
}
Can someone give a better solution for this.
https://dotnetfiddle.net/gcd1I1
Thanks!
You can pass a Comparison to Sort method, then compare using absolute value:
list.Sort((a, b) => Math.Abs(a).CompareTo(Math.Abs(b)));
Or using Linq:
list = list.OrderBy(x => Math.Abs(x)).ToList();

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.

Splitting an array using LINQ

I have a collection uni-dimensional like this:
[1,2,4,5.....n]
I would like to convert that collection in a bi-dimensional collection like this:
[[1,2,3],
[4,5,6],
...]
Basically I want to group or split if you want, the array in groups of 'n' members
I can do it with a foreach statement, but I am currently learning LINQ so instead of iterating through all elements and create a new array manually I would like to use the LINQ features (if applicable)
Is there any LINQ function to help me to accomplish this??
I was thinking in the GroupBy or SelectMany I do not know if they will help me though but they might
Any help will be truly appreciate it =) :**
You can group by the index divided by the batch size, like this:
var batchSize = 3;
var batched = orig
.Select((Value, Index) => new {Value, Index})
.GroupBy(p => p.Index/batchSize)
.Select(g => g.Select(p => p.Value).ToList());
Use MoreLinq.Batch
var result = inputArray.Batch(n); // n -> batch size
Example
var inputs = Enumerable.Range(1,10);
var output = inputs.Batch(3);
var outputAsArray = inputs.Batch(3).Select(x=>x.ToArray()).ToArray(); //If require as array
You want Take() and Skip(). These methods will let you split an IEnumerable. Then you can use Concat() to slap them together again.
The sample below will split an array into groups of 4 items each.
int[] items = Enumerable.Range(1, 20).ToArray(); // Generate a test array to split
int[][] groupedItems = items
.Select((item, index) => index % 4 == 0 ? items.Skip(index).Take(4).ToArray() : null)
.Where(group => group != null)
.ToArray();
It's not a pure LINQ but it's intended to be used with it:
public static class MyEnumerableExtensions
{
public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, int size)
{
if (source == null)
{
throw new ArgumentNullException("source can't be null.");
}
if (size == 0)
{
throw new ArgumentOutOfRangeException("Chunk size can't be 0.");
}
List<T> result = new List<T>(size);
foreach (T x in source)
{
result.Add(x);
if (result.Count == size)
{
yield return result.ToArray();
result = new List<T>(size);
}
}
}
}
It can be used from your code as:
private void Test()
{
// Here's your original sequence
IEnumerable<int> seq = new[] { 1, 2, 3, 4, 5, 6 };
// Here's the result of splitting into chunks of some length
// (here's the chunks length equals 3).
// You can manipulate with this sequence further,
// like filtering or joining e.t.c.
var splitted = seq.Split(3);
}
It's as simple as:
static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> ToPages<T>(this IEnumerable<T> elements, int pageSize)
{
if (elements == null)
throw new ArgumentNullException("elements");
if (pageSize <= 0)
throw new ArgumentOutOfRangeException("pageSize","Must be greater than 0!");
int i = 0;
var paged = elements.GroupBy(p => i++ / pageSize);
return paged;
}
}
I based my solution of Jeremy Holovacs's answer and used Take() and Skip() to create subarrays.
const int batchSize = 3;
int[] array = new int[] { 1,2,4,5.....n};
var subArrays = from index in Enumerable.Range(0, array.Length / batchSize + 1)
select array.Skip(index * batchSize).Take(batchSize);
Starting with .NET 6, there is the System.Linq.Enumerable.Chunk(this IEnumerable<TSource>, int size) extension method. It returns an IEnumerable<TSource[]> where each item is an array of size elements, except the last item, which could have fewer.
Code like this:
using System;
using System.Collections.Generic;
using System.Linq;
int[] input = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<int[]> chunks = input.Chunk(3);
foreach (int[] chunk in chunks)
{
foreach (int i in chunk)
{
Console.Write($"{i} ");
}
Console.WriteLine();
}
outputs
1 2 3
4 5 6
7 8 9
10

Categories