Sort int array using linq? - c#

PROBLEM
Write a program to sort any given integer array of positive and negative numbers such that positive numbers follow negative numbers, however relative position of positive and negative numbers remains same. Like in the example below (1), (-7) occurs after (-5) hence in sorted array (-7) follows (-5).
The program should iterate given array only once and should not use temporary array.
Example 1.
Given Array: { 2, 4, -5, 0, -7, -2, 2, -5, 1, -9, -7, -1 }
Sorted Array: { -5, -7, -2, -5, -9, -7, -1, 2, 4, 0, 2, 1 }
Example 2.
Given Array: { -3, 7, 0, -2, -1, 3, -3, 9, 5, -5, 8}
Sorted Array: { -3, -2, -1, -3, -5, 7, 0, 3, 9, 5 , 8}
MY SOLUTION
class Program
{
public static void Main(string[] args)
{
var intArray = new int[] { 2, 4, -5, 0, -7, -2, 2, -5, 1, -9, -7, -1 };
var sorted_intArray = SortIntArray(intArray);
Console.WriteLine(String.Join(",", sorted_intArray));
intArray = new int[] { -3, -2, -1, -3, -5, 7, 0, 3, 9, 5, 8 };
sorted_intArray = SortIntArray(intArray);
Console.WriteLine(String.Join(",", sorted_intArray));
Console.ReadLine();
}
private static int[] SortIntArray(int[] intArray)
{
var sorted_intArray = intArray.GroupBy(_int => _int < 0 ? -1 : 1)// Create group of +ve and -ve numbers
.OrderBy(group => group.Key) // Bring group of -ve number first
.SelectMany(groupedItems => groupedItems).ToArray(); // Select num from both Group and convert to array
return sorted_intArray;
}
}
QUESTION
i) The problem statement says , numbers should be iterated only once and temporary array should not be used. I believe my solution using linq do not meet this requirement. How to solve mentioned problem with linq , if possible?
ii) What could be other possible and efficient ways to solve mentioned problem with or without linq? Solution using C/C++ is also fine.

According to this OrderBy is stable so:
enumerable.OrderBy(x => x >= 0);

I like these one:
For Order By Ascending
enumerable.OrderBy(x => x);
For Order By Descending
enumerable.OrderByDescending(x => x);

Linq is amazing:
private static int[] SortIntArrayWithDuplicates(IEnumerable<int> intArray)
{
var enumerable = intArray as int[] ?? intArray.ToArray();
return enumerable.Where(x => x < 0)
.Concat(enumerable.Where(x => x >= 0))
.ToArray();
}
private static int[] SortIntArrayWithoutDuplicates(IEnumerable<int> intArray)
{
var enumerable = intArray as int[] ?? intArray.ToArray();
return enumerable.Where(x => x < 0)
.Union(enumerable.Where(x => x >= 0))
.ToArray();
}

You don't need grouping at all. Linq-to-objects is "stable" (meainig it keeps the original order for "duplicate" objects) when using OrderBy, so you can just order on whether or not the number is less than zero:
private static int[] SortIntArray(int[] intArray)
{
var sorted_intArray = intArray.OrderBy(i => i < 0 ? 0 : 1).ToArray();
return sorted_intArray;
}
Output:
-5,-7,-2,-5,-9,-7,-1,2,4,0,2,1
-3,-2,-1,-3,-5,7,0,3,9,5,8
Note that if you change your return type to IEnumerable<int> then you can avoid the ToArray call and reduce your memory usage.
Whether or not you count a Linq ordering as "iterated once" is up to whoever gave you that requirement. I don't know of any sorting algorithm that can be done in one pass.
What could be other possible and efficient ways to solve mentioned problem with or without linq?
Well I can see how you'd do it in two passes by scanning all numbers, taking negative ones first, then scan again, taking all non-negative ones. You can simplify this with yield syntax:
private static IEnumerable<int> SortIntArray(int[] intArray)
{
foreach(var i in intArray)
if(i < 0) yield return i;
foreach(var i in intArray)
if(i >= 0) yield return i;
}

Related

How to get index of all values of not sorted array based on lower value C#

I am currently working on a simulation system so i've an array like
int[] arr = {2,5,9,10,0, 4,1,5,3};
I want array of indexes of values based on lower and output result like
result = {4, 6, 0, 8, 1, 7, 2, 3};
I searched all over for almost 3 days i can't find.
In the 2nd array you want the indexes of the elements in the first array as sorted in ascending order.
You can use LINQ to do this
int[] arr = { 2, 5, 9, 10, 0, 4, 1, 5, 3 };
int[] result = arr.Select((x, i) => (x, i))
.OrderBy(t => t.x)
.Select(t => t.i)
.ToArray();
Here, we used an overload of Select that yields the index:
Select<TSource,TResult>(IEnumerable, Func<TSource,Int32,TResult>).
The first Select creates a ValueTuple.
The test
Console.WriteLine(String.Join(", ", result));
Yields the result:
4, 6, 0, 8, 5, 1, 7, 2, 3
Note that the number 5 appears twice in the input array. Therefore, the result is ambiguous. (Your expected result has only 8 indexes but the input array has a length of 9)
My full .NET 6.0 test code (Console App):
namespace CoreConsoleApp;
internal static class SortedArrayIndexes
{
public static void Test()
{
// Indexes 0 1 2 3 4 5 6 7 8
int[] arr = { 2, 5, 9, 10, 0, 4, 1, 5, 3 };
int[] result = arr.Select((x, i) => (x, i))
.OrderBy(t => t.x)
.Select(t => t.i)
.ToArray();
Console.WriteLine(String.Join(", ", result));
}
}
It is called in my Main method with:
SortedArrayIndexes.Test();
Console.ReadKey();
In case you are working with an older Framework or language version, you can also use the older System.Tuple Class
int[] result = arr.Select((x, i) => new Tuple<int, int>(x, i))
.OrderBy(t => t.Item1)
.Select(t => t.Item2)
.ToArray();
We can use the built-in Array.Sort() method. It has a second parameter that returns the sorted indices as well, but it requires us to make a new array with the indices (i.e. starts from 0 and ends at the length of the original array)
int[] arr = {2,5,9,10,0,4,1,5,3};
int[] indexArr = Enumerable.Range(0, arr.Length).ToArray();
Array.Sort(arr, indexArr);
Note that you will need to add using System.Linq; if you get an error that Enumerable doesn't exist.

How to convert a sequence of points to a sequence of ranges?

For example, I have a sequence of points
List points = new List {0, 1, 2, 4, 5 ,7};
And I want to convert it to a sequence of ranges (My type Range(leftPoint, rightPoint)). For the example, results are
List<Range> ranges: {0, 1} {1, 2} {2, 4} {4, 5} {5, 7}
You could use LINQ (presuming the list is already sorted):
List<Range> rangeList = Enumerable.Range(0, points.Count - 1)
.Select(i => new Range(points[i], points[i + 1]))
.ToList();
Why not just use a simple for-loop?
for(var i = 0; i < points.Count() - 1; i++)
ranges.Add(new Range(points[i], points[i+1]))
List<int> points = new List<int> { 0, 1, 2, 4, 5, 7 };
List<List<int>> listOfRanges = new List<List<int>>();
listOfRanges.Add(points.GetRange(0, 2));
listOfRanges.Add(points.GetRange(1, 2));
listOfRanges.Add(points.GetRange(2, 2));
listOfRanges.Add(points.GetRange(3, 2));
listOfRanges.Add(points.GetRange(4, 2));
You can iterate over it like so:
List<int> points = new List {0, 1, 2, 4, 5 ,7};
List<Range> listOfRanges = new List<Range>();
int index = 0
foreach (int value in points) {
listOfRanges.add(new Range(points[i], points[i+1]));
index++;
}
You might get a null comparison exception on the last iteration of the loop as points[i+1] doesn't exist - if so just handle this with a simple if statement.
This is assuming by points you mean a list of integers. I'll update if I find you meant something different.
You can use LINQ to zip the two lists that we get when we:
Take everything except the last element
Take everything except the first element
These correspond to (in the case of your example):
0, 1, 2, 4, 5
1, 2, 4, 5 ,7
Here is how you do it in code:
var result =
points
.Take(points.Count - 1) //points except last element
.Zip(
points.Skip(1), //points except first element
(l, r) => new Range(l, r))
.ToList();

Sort list based on multiple conditions

I have a list of integer lists, like that:
A -> 10 10 1 1 1
B -> 10 9 9 7 6
...
I would like to sort them based on how many 10s they have, then on how many 9s, 8s, 7s, and so on untile the 1s
So in the example above A should be better than B because even if it has less total points, it has two 10s instead of only 1.
Code should be generic because I don't know how many numbers will be available for each case (sometimes 10, sometimes 5, or even only 3).
I developed something like that:
lists.OrderByDescending(a => a.Where(b => b == 10).Count()).
ThenByDescending(a => a.Where(b => b == 9).Count()).
and so on, but this is not generic...
I hope the question is clear... thank you very much!
You can create query which orders lists by count of 10s, then compose query by adding additional orderings for numbers from 9 to 1:
var query = lists.OrderByDescending(l => l.Count(x => x == 10));
for (int i = 9; i >= 1; i--)
query = query.ThenByDescending(l => l.Count(x => x == i));
For these sample lists:
var lists = new[] {
new[] { 10, 9, 9, 8, 7 },
new[] { 10, 9, 9, 7, 6 },
new[] { 10, 10, 1, 1, 1 }
};
Result will be:
[10, 10, 1, 1, 1]
[10, 9, 9, 8, 7]
[10, 9, 9, 7, 6]
It's simple, but not very efficient. If you need better performance, then consider creating custom comparer. Here is sample with comparer which uses zipped ordered sequences to check if all items in sequences are same, or get first item which is different:
public class CustomComparer : Comparer<IList<int>>
{
public override int Compare(IList<int> x, IList<int> y)
{
var comparisons = x.Zip(y, (a,b) => a.CompareTo(b));
foreach(var comparison in comparisons)
{
if (comparison != 0)
return comparison;
}
return x.Count.CompareTo(y.Count);
}
}
NOTE: If items in lists are not ordered, then you should sort them before zipping:
var comparisons =
x.OrderByDescending(i => i)
.Zip(y.OrderByDescending(i => i), (a,b) => a.CompareTo(b));
It works very simple. Consider two lists:
[10, 9, 9, 8, 7, 5]
[10, 9, 9, 7, 6]
It will create pairs of items in corresponding positions:
{10,10}, {9,9}, {9,9}, {8,7}, {7,6}
Then items in each pair will be compared one by one, until first mismatch will be found:
0, 0, 0, 1 (four comparisons only)
That means first list has more 8s than second one. Usage:
var query = lists.OrderByDescending(i => i, new CustomComparer());
Result is same.
The following comparer
public class Comparer : IComparer<IEnumerable<int>>
{
public int Compare(IEnumerable<int> a, IEnumerable<int> b)
{
var aOrdered = a.OrderByDescending(i => i).Concat(new[] { int.MinValue });
var bOrdered = b.OrderByDescending(i => i).Concat(new[] { int.MinValue });
return a.Zip(b, (i, j) => i.CompareTo(j)).FirstOrDefault(c => c != 0);
}
}
lets you order you lists of lists like so
var result = lists.OrderByDescending(i => i, new Comparer());
without iterating through each list ten times counting individual elements.
This compares the lists and returns conventional comparison result - 1, 0, or -1 is returned depending on whether one value is greater than, equal to, or less than the other.
static int CompareLists(List<int> a, List<int> b)
{
var grpA = a.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
var grpB = b.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
for (int i = 10; i >= 0; i--)
{
int countA = grpA.ContainsKey(i) ? grpA[i] : 0;
int countB = grpB.ContainsKey(i) ? grpB[i] : 0;
int comparison = countA.CompareTo(countB);
if (comparison != 0)
return comparison;
}
return 0;
}
First we convert the lists into dictionary of number->amount of occurences.
Then we iterate through numbers from 10 to 0 and compare the number of occurences. If the result is 0, then we go to another number.
If you have List<List<int>> to sort, just use list.Sort(CompareLists) as in:
List<int> d = new List<int> { 10, 6, 6 };
List<int> b = new List<int> { 10, 9, 9 };
List<int> a = new List<int> { 10, 10, 1, 1, 1 };
List<int> c = new List<int> { 10, 7, 7 };
List<int> e = new List<int> { 9, 3, 7 };
List<int> f = new List<int> { 9, 9, 7 };
List<List<int>> list = new List<List<int>>() { a, b, c, d, e, f };
list.Sort(CompareLists);

Reordering an array in a customized way

I have an array of items that prints to pdf in the following order.
Lets say for eg:
lines = {1, 2, 3,
4, 5, 6,
7, 8, 9,
10}
is the content of my array.
However I want to change the order of the items in the array to
{1, 4, 7,
2, 5, 8,
3, 6, 9,
10}
Then I pass this array to my print engine. Basically if there are more than 3 items in the array, my new code should reorder it.
Could somebody help me figuring out the logic for that.
Thanks
Order the lines by the modulus of the line index with the number of rows.
public static ICollection<T> Sort<T>(ICollection<T> lines, int columns)
{
var rows = lines.Count/columns;
if (rows == 0)
{
return lines;
}
return lines.Select((line, i) => new {line, i})
.OrderBy(item => item.i < columns*rows ? item.i%rows : rows)
.Select(item => item.line)
.ToList();
}
Edit: Alternatively you can use an iterator method and the list's indexer instead of LINQ:
public static IEnumerable<T> Sort<T>(IList<T> lines, int columns)
{
var rows = lines.Count/columns;
for (var i = 0; i < lines.Count; i++)
{
var index = rows > 0 && i < columns*rows
? (i%columns)*rows + i/columns
: i;
yield return lines[index];
}
}
Assuming "for linear array assuming every 9 elements form 3x3 matrix transpose each subsequence, keep remainder as-is":
// assuming T[] items;
var toTranspose = (items.Count() / 9) * 9;
var remap = new int[]{1, 4, 7, 2, 5, 8, 3, 6, 9 };
var result = Enumerable.Range(0, toTranspose)
.Select(pos => items[(pos / 9) * 9 + (remap[pos % 9] - 1)])
.Concat(items.Skip(toTranspose)
.ToArray();
Summary of code:
get number of items that need to be moved (which is number of groups of 9 items int numberOfGroup = Count()/9;, multiplied by group size)
have custom transformation in remap array (note that indexes copied as-is from sample and actually off-by-one hence -1 in computing index)
for each element index under toTranspose get source element from corresponding group and apply transformation with remap.
finally Concat the remainder.
Notes:
one can easily provide custom transformation or inline transposition if needed.
can't apply transformation to the last partial group as elements will have to go to non-existent positions.

To find the top 3 maximum repeated numbers in a integer array

I want to find the top 3 maximum repeated numbers in a Integer array?
Below is the piece of code which I have tried but I couldn't find the desired result:
static void Main(string[] args)
{
int[,] numbers = {
{1, 2, 0, 6 },
{5, 6, 7, 0 },
{9, 3, 6, 2 },
{6, 4, 8, 1 }
};
int count = 0;
List<int> checkedNumbers = new List<int>();
foreach (int t in numbers)
{
if (!checkedNumbers.Contains(t))
{
foreach (int m in numbers)
{
if (m == t)
{
count++;
}
}
Console.WriteLine("Number {0} is Repeated {1} Times ", t, count);
count = 0;
checkedNumbers.Add(t);
}
}
Console.ReadLine();
}
You can use GroupBy from LINQ then OrderByDescending based on count in each group:
var result = list.GroupBy(i => i)
.OrderByDescending(g => g.Count())
.Select(g => g.Key)
.Take(3);
Edit: With your code, you can use OfType to flatten your matrix then use the code above:
int[,] numbers = {
{1, 2, 0, 6 },
{5, 6, 7, 0 },
{9, 3, 6, 2 },
{6, 4, 8, 1 }
};
var list = numbers.OfType<int>();
int[] numbers = {1, 2, 3, 5, 6, 32, 2, 4, 42, 2, 4, 4, 5, 6, 3, 4};
var counts = new Dictionary<int, int>();
foreach (var number in numbers)
{
counts[number] = counts[number] + 1;
}
var top3 = counts.OrderByDescending(x => x.Value).Select(x => x.Key).Take(3);
Hint:
You can do this with the help of LINQ.
This is the code to find most frequest occuring element:-
List<int> list = new List<int>() { 1,1,2,2,3,4,5 };
// group by value and count frequency
var query = from i in list
group i by i into g
select new {g.Key, Count = g.Count()};
// compute the maximum frequency
int frequency = query.Max(g => g.Count);
// find the values with that frequency
IEnumerable<int> modes = query
.Where(g => g.Count == frequency)
.Select(g => g.Key);
// dump to console
foreach(var mode in modes) {
Console.WriteLine(mode);
}
In the same manner you can find the other two also.
I see that none of the existing answers provide an explanation, so I will try to explain.
What you need to do is to count how many times each item appears in the array. To do that, there are various methods (dictionaries, linq etc). Probably it would be easiest to use a dictionary which contains the number, and how may times it appeared:
int numbers[] = {1, 3, 6, 10, 9, 3, 3, 1, 10} ;
Dictionary<int, int> dic = new Dictionary<int, int>();
Now iterate through every element in numbers, and add it to the dictionary. If it was already added, simply increase the count value.
foreach (var i in numbers)
{
dic[i]++; // Same as dic[i] = dic[i]+1;
}
The dictionary will automatically adds a new item if it doesn't exist, so we can simply do dic[i]++;
Next, we need to get the highest 3 values. Again, there are many ways to do this, but the easiest one would be to sort it.
var sorted_dic = dic.OrderByDescending(x => x.Value);
Now the first 3 items in sorted_dic are going to be the 3 values you are looking for.
There are various methods to get only these 3, for example using the Take method:
var first_3 = sorted_dic.Take(3);
Now you can iterate through these 3 values, and for example print them on the screen:
foreach (var i in first_3)
{
Console.Write("{0} appeared {1} times.", i.Key, i.Value);
}

Categories

Resources