Get the order of bytes by value - c#

I have an interesting thought-problem right now, so maybe someone of you could help.
Basically what I have is an array of bytes and need to know the order of each individual item in that array - by value. Oh man, i will just show you a small example, i think that will help better:
byte[] array = new byte[4] { 42, 5, 17, 39 }
I want to get this as a result
4 1 2 3
What would be a smart way to do this?
In addition i would like the algorithm to retain the order, if the entries are valued equal. So
new byte[4] { 22, 33, 33, 11 }
should result in
2 3 4 1

You can use:
byte[] byteArray = new byte[4] { 42, 5, 17, 39 };
var results = byteArray.Select((b, i) => new { Value = b, Index = i})
.OrderBy(p => p.Value)
.Select((p, i) => new { Index = p.Index, SortedPosition = i + 1 })
.OrderBy(p => p.Index)
.Select(p => p.SortedPosition);

byte[] array = new byte[4] { 42, 5, 17, 39 }
var sorted = array.OrderBy(x=> x);
var result = from b in sorted
select (x => array.IndexOf(x) + 1);
As noted, this does not handle duplicates.

Related

Zipping two lists in C#

I have two lists of Integer values:
List<int> list1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> list2 = new List<int>() { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
I want to zip the above two lists so that the elements in even index are obtained from the sum of corresponding elements in List 1 and List 2 and the odd elements are obtained by multiplying them, I tried to do something like this but it did't work:
list1.Zip(list2, index => index % 2 == 0 ? (a, b) => a + b : (a, b) => a * b );
desired output
{ 12,24,16,56,20,96,24,144,28,200 }
You can Zip first then use the overload of Select that includes the index.
var result = list1.Zip(list2, (a,b) => (A:a, B:b))
.Select((x, i) => i % 2 == 0 ? x.A + x.B : x.A * x.B);
Note I'm using value tuples here, so depending on your version of C# you might need to use anonymous classes instead (a,b) => new { A=a, B=b }
Zip method doesn't have overload which supports index, you can use MoreLinq library or Select method instead (with element selector, which supports index)
var result = list1.Select(
(value, index) =>
index % 2 == 0 ? value + list2[index] : value * list2[index])
.ToList();
result.ForEach(Console.WriteLine);
It'll work if both lists have the same length and give you an expected output 12, 24, 16, 56, 20, 96, 24, 144, 28, 200
Another option is to Zip both lists into list of anonymous objects, then calculate the sum against them
var result = list1
.Zip(list2, (a, b) => new { a, b })
.Select((value, index) => index % 2 == 0 ? value.a + value.b : value.a * value.b)
.ToList();
Use .Select() instead of Zip
var result = list1.Select((e, i) =>
i % 2 == 0 ? e + list2[i] : e * list2[i])
.ToList()
Another option:
int index = 0;
var zipped = list1.Zip(list2, (a, b) => index++ % 2 == 0 ? a + b : a * b);
Succinctly, but it uses a side effect, which is of course bad.

Serialize/Deserialize with a specified syntax: {1, 2, 3, 5, 8, 9} <--> "1-3, 5, 8-9"

This is a human-readable serialization syntax of integer array.
The serialized string implies some integers, the consistent integers will be compressed with {start}-{end}, and the "unconsistent" elements will be split by comma.
Now, I want to serialize a sorted int[] to the string, and deserialize a string to the int[].
How can I achieve it concisely?
This is a related question: Split a List<int> into groups of consecutive numbers
I have an imperfect solution to serialize:
public string SerializationTest()
{
var weeks = new[] { 1, 2, 3, 5, 8, 9 };
return string.Join(", ", weeks
.Select((e, i) => (e, i))
.GroupBy(t => t.i - t.e)
.Select(tg => tg.Select(t => t.e).ToArray())
.Select(group => group.Length > 1 ? $"{group.First()}-{group.Last()}" : $"{group.Single()}"));
}
But there is no idea about deserialization, except switch every character.
Choosing whichever implementation from your previous question then:
var numbers = new[] { 1, 2, 3, 4, 6, 7, 9 };
var groups = numbers.GroupConsecutive();
var serialized = string.Join(", ", groups.Select(i => i.Skip(1).Any() ?
$"{i.First()}-{i.Last()}" : $"{i.First()}"));
var deserialized = serialized.Split(new string[] { ", "}, StringSplitOptions.None)
.Select(i => i.Split('-').Select(s => int.Parse(s)).ToArray())
.SelectMany(i => i.Length == 1 ? i : Enumerable.Range(i[0], i[1] - i[0] + 1)).ToList();

Sorting a list in ascending and descending order

I have a collection of integers e.g.
INPUT EXAMPLE
4,7,9,8,20,56,78,34,2,76,84,98
I need to sort this list in a way that any number til 20 will be sorted in ascending order and above 20 will be sorted in descending order. So output will be :
OUTPUT EXAMPLE
2,4,7,8,9,20,98,84,78,76,56,34
I wrote a comparer for it. but now trying for more clean approach may be by using the existing tools like orderby.
You can do that using two sort groups:
list.OrderBy(i => i <= 20 ? i : int.MaxValue) // sort numbers less than 20 ascending; put numbers greater than 20 at the end
.ThenByDescending(i => i) // sort remaining numbers descending
You can trivially do this using a custom comparer:
public class MyCustomComparer : IComparer<int>
{
private readonly int _cutOffPointInclusive;
public MyCustomComparer(int cutOffPointInclusive)
{
_cutOffPointInclusive = cutOffPointInclusive;
}
public int Compare(int x, int y)
{
if (x <= _cutOffPointInclusive || y <= _cutOffPointInclusive)
{
return x.CompareTo(y);
}
else
{
return y.CompareTo(x);
}
}
}
This sorts ascendingly when either value to compare is lower than or equal to the cutoff point (both to push the greater values to the top and to sort the values up till the cutoff point ascendingly), and descendingly when both are greater than the cutoff point (to actually sort those greater values descendingly).
Tested using:
var testData = new List<int>{ 4,7,9,8,20,56,78,34,2,76,84,98 };
testData.Sort(new MyCustomComparer(20));
foreach (var i in testData)
{
Console.WriteLine(i);
}
Output:
2
4
7
8
9
20
98
84
78
76
56
34
See also http://ideone.com/YlVH8i. So I don't really think this isn't "clean", but just fine.
Why don't use two steps?
var bellow = originallist.Where(i => i <= 20).OrderBy(i);
var above= originallist.Where(i => i > 20).OrderByDescending(i);
var sorted = bellow.Concat(above).ToList();
int[] a = { 4, 7, 9, 8, 20, 56, 78, 34, 2, 76, 84, 98 };
var b = a.OrderBy(i => i > 20 ? int.MaxValue - i : i);
If possible, I recommend sorting in-place. For example ( can be improved )
Array.Sort(a, (i1, i2) => (i1 > 20 ? int.MaxValue - i1 : i1) - (i2 > 20 ? int.MaxValue - i2 : i2));
[Test]
public void SortTill20AscRestDesc()
{
var src = new[] {4, 7, 9, 8, 20, 56, 78, 34, 2, 76, 84, 98};
var expected = new[] {2, 4, 7, 8, 9, 20, 98, 84, 78, 76, 56, 34};
var result = src
.Select(
i => new
{
IsAbove20 = i > 20,
Value = i
}
)
.OrderBy(e => e.IsAbove20)
.ThenBy(e => e.IsAbove20 ? int.MaxValue : e.Value)
.ThenByDescending(e => e.Value)
.Select(e=>e.Value);
Assert.That(result.SequenceEqual(expected), Is.True);
}

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);

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