Linq one Result from multiple Lists - c#

I have one method that calculates a score. simplified:
public static int GetScore(int v1, int v2, char v3)
{
//calculate score
return score;
}
v1, v2 and v3 are 3 values from 3 lists:
List<int> Values1 = new List<int>();
List<int> Values2 = new List<int>();
List<char> Values3 = new List<char>();
//fill Values1, Values 2, Values3
How must be the Select to determine each combination of the three lists and determine the highest score? I thought of something like that:
int MaxScore = Values1.Select(x => Values2.Select(y => GetScore(x, y))).Max(); // ???
My current approach
int MaxScore = 0;
foreach (int x in Values1)
{
foreach (int y in Values2)
{
foreach (char z in Values3)
{
int Score = GetScore(x, y, z);
if (Score > MaxScore)
{
MaxScore = Score;
}
}
}
}

In this case I think LINQ Query syntax is more clear.
var data = from v1 in Values1
from v2 in Values2
from v3 in Values3
select GetScore(v1, v2, v3);
var max = data.Max();

As per the suggestion in my comment, you can do this:
int MaxScore =
Values1
.SelectMany(x =>
Values2
.SelectMany(y =>
Values3
.Select(z =>
GetScore(x, y, z))))
.Max();

Related

Sorting array by frequency of elements in C#

Here is what i have so far
int[] numbers = { 3,5,4,3,8,8,5,3,2,1,9,5 };
int[] n = new int[12];
int[] k;
foreach (int number in numbers)
{
n[number]++;
}
Array.Sort(n);
Array.Reverse(n);
foreach (int value in n)
{
Console.WriteLine(value);
}
I know i am missing the part where i sort the frequency of the elements after i counted them and i just cant get my head around it. I'd appreciate some help, Thanks!
What's the problem with your solution ?
Whereas you correctly keep the frequencies of the numbers in the table called n in your code, which hereby I would call it frequencies, then you Sort this array. This action breaks your solution, since each frequency is associated with the corresponding index of its location in the array.
E.g. If an instance of this array is this [8,2,1,7,6]. When you call the Sort method on this array, this would have as a result the array to be sorted and the order of the elements of the array would be this [1,2,7,6,8]. Before calling sort, the first element of the array was indicating that the number 0 (the index of the first element is 0) has been found 8 times in our numbers. After sort, the first element is 1, which means now that the frequency of the number 0 is 1, which is apparently wrong.
If you want to keep it your way, then you could try something like this:
int[] numbers = { 1,2,2,9,1,2,5,5,5,5,2 };
int[] frequencies = new int[12];
int k = 3;
foreach (int number in numbers)
{
frequencies[number]++;
}
var mostFrequentNumbers = frequencies.Select((frequency, index) => new
{
Number = index,
Frequency = frequency
})
.OrderByDescending(item => item.Frequency)
.Select(item => item.Number)
.Take(k);
foreach (int mostFrequentNumber in mostFrequentNumbers)
{
Console.WriteLine(mostFrequentNumber);
}
Are there any other approaches ?
An easy way to do this is to use a data structure like a Dictionary, in which you would keep as keys the numbers and as the corresponding values the corresponding frequencies.
Then you can order by descending values the above data structure an keep the k most frequent numbers.
int[] numbers = { 1,2,2,9,1,2,5,5,5,5,2 };
int k = 3;
Dictionary<int, int> numberFrequencies = new Dictionary<int, int>();
foreach (int number in numbers)
{
if(numberFrequencies.ContainsKey(number))
{
numberFrequencies[number] += 1;
}
else
{
numberFrequencies.Add(number, 1);
}
}
var mostFrequentNumbers = numberFrequencies.OrderByDescending(numberFrequency => numberFrequency.Value)
.Take(k)
.Select(numberFrequency => numberFrequency.Key);
foreach (int mostFrequentNumber in mostFrequentNumbers)
{
Console.WriteLine(mostFrequentNumber);
}
You can also achieve the same thing by only using LINQ:
int[] numbers = { 1,2,2,9,1,2,5,5,5,5,2 };
int k = 3;
var mostFrequentNumbers = numbers.GroupBy(number => number)
.ToDictionary(gr => gr.Key, gr => gr.Count())
.OrderByDescending(keyValue => keyValue.Value)
.Take(k)
.Select(numberFrequency => numberFrequency.Key);
foreach (int mostFrequentNumber in mostFrequentNumbers)
{
Console.WriteLine(mostFrequentNumber);
}
You can just use Linq extensions:
using System.Linq;
using System.Collections.Generic;
...
private static IEnumerable<int> Solve(int[] numbers, int k) {
return numbers
.GroupBy(x => x)
.OrderByDescending(g => g.Count())
.Select(g => g.Key)
.Take(k);
}
Then you can call:
var numbers = new []{1,2,2,9,1,2,5,5,5,5,2};
var k = 3;
var result = Solve(numbers, k);
foreach (int n in result)
Console.WriteLine(n);
To be very terse:
var frequents = numbers.GroupBy(t => t)
.Where(grp => grp.Count() > 1)
.Select(t => t.Key)
.OrderByDescending(t => t)
.Take(k)
.ToList();

Cleaning List<double[]> within a given tolerance

I have a List where each double[] has a length of 3. I would like to clean this list by leaving only those double[] having unique elements within a given tolerance (round up). For instance, a list like the one below:
1059.17 0 446.542225842081
1059.17 0 446.542564789741
1059.17 0 446.541759880305
959.167 0 579.827860527898
959.167 0 579.827847296075
Should become this for a given tolerance=two:
1059.17 0 446.54,
959.17 0 579.83,
Is there a smart way to do this in a neat way?
This should work. It uses the build-in equality comparisons of anonymous types.
List<double[]> data = ...
int tolerance = 2;
var roundedData = data
.Select(x => new {
v1 = Math.Round(x[0], tolerance),
v2 = Math.Round(x[1], tolerance),
v3 = Math.Round(x[2], tolerance)
})
.Distinct()
.Select(x => new [] { x.v1, x.v2, x.v3 })
.ToList();
Providing that array elements are always in the same order you can create your own comparer that should know how to compare double arrays :
public class MyDoubleArrComparer : IEqualityComparer<double[]>
{
public bool Equals(double[] x, double[] y)
{
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i]) return false;
}
return true;
}
public int GetHashCode(double[] obj)
{
return base.GetHashCode();
}
}
And you can create a helper method that will round numbers and remove duplicates :
public static class Helper
{
public static List<double[]> MyFilter(this List<double[]> list, int tolerance)
{
var result = list
.Select(arr =>
{
// rounds numbers with precision that is set in tolerance variable
arr = arr.Select(d => d = Math.Round(d, tolerance)).ToArray();
return arr;
}).Distinct(new MyDoubleArrComparer()) // here we use our custom comparer
.ToList();
return result;
}
}
Now we can start using our helper method :
var nums = new List<double[]>()
{
new[] {1059.17, 0, 446.542225842081},
new[] {1059.17, 0, 446.542564789741},
new[] {1059.17, 0, 446.541759880305},
new[] {959.167, 0, 579.827860527898},
new[] {959.167, 0, 579.827847296075},
};
var result = nums.MyFilter(2);
foreach (var arr in result)
{
foreach (var d in arr)
{
Console.Write(d + " ");
}
Console.WriteLine();
}
Output :
1059.17 0 446.54
959.17 0 579.83
Maybe this will work?
public List<double[]> CleanWithTolerance(List<double[]> doubleNumbersList, int tolerance)
{
var newDoublesNumbersList = new List<double[]>();
foreach(double[] doubleNumbers in doubleNumbersList)
{
var newDoublesNumbers = doubleNumbers.Select(doubleNumber => Math.Round(doubleNumber, tolerance)).ToArray();
if(newDoublesNumbersList.All(cleanDoubleNumbers => !Enumerable.SequenceEqual(cleanDoubleNumbers, newDoublesNumbers))
{
newDoublesNumbersList.Add(newDoublesNumbers);
}
}
return newDoublesNumbersList;
}

Group list into groups by the the index range of the list

I have a source integer list with numbers from 0 to 50.
Then I want to have a grouped target list that means:
group1: 1,2,3,4,5,6,7,8,9,10
group2: 11,12,13,14,15,16,17,18,19,20
group3: etc... ,30
group4: etc... ,40
group5: etc... ,50
The groupFactor here is 5.
How can I group my integer list basing on that group factor which could be any number?
UPDATE
If the group factor is 6
there would be an additional:
group6: etc... ,60
Let k be your group factor. Group your list by multiplying the list member by k then dividing by 50, and grouping the sequence on the resulting quotient.
Your question is a little vague but for the sample you provided, I found this fancy group by :)
var list = new List<int>();
for (int i=0; i <= 50; i++)
{
list.Add(i);
}
var result = list.GroupBy( n => (n-1)/10 );
Try this
static void Main(string[] args)
{
List<int> input = new List<int>();
for (int i = 0; i <= 50; i++)
{
input.Add(i);
}
List<List<int>> output = input.Select((x, i) => new { x = x, i = (int)(x / 10) }).GroupBy(y => y.i).Select(z => z.Select(a => a.x).ToList()).ToList();
}

Grouping list elements to dictionary

I have a list that contains 8 elements:
ConfigFile.ControllerList
this list is type of:
List<Controller>
How can i add Controllers from ControllerList to 3 dictionary keys. Dictionary is like:
Dictionary<int, List<Controller>> ControllerDictionary = new Dictionary<int, List<Controller>>();
I want to add first 3 controllers to dictionary key 0, then want to add next 3 controllers to dictionary key 1 and lastly want to add last 2 controllers to dictionary key 2. How can i do that?
You can use / to split the list into sub-list:
var ControllerDictionary = ControllerList
.Select((c, i) => new { Controller = c, Index = i })
.GroupBy(x => x.Index / maxGroupSize)
.Select((g, i) => new { GroupIndex = i, Group = g })
.ToDictionary(x => x.GroupIndex, x => x.Group.Select(xx => xx.Controller).ToList());
The idea is to first group the elements by indexes, then divide them by an int maxGroupSize(in your case 3). Then convert each group to a list.
Not sure if there's a more elegant solution, but something like this should work:
var dict = new Dictionary<int, List<Controller>>();
int x = 0;
while (x < controllerList.Count)
{
var newList = new List<Controller> { controllerList[x++] };
for (int y = 0; y < 2; y++) // execute twice
if (x < controllerList.Count)
newList.Add(controllerList[x++]);
dict.Add(dict.Count, newList);
}
To make it more general, you could also create newList empty to start, and then change y < 2 to y < GROUP_SIZE where GROUP_SIZE is whatever sized groups you want. Could even then extract this to an extension method:
public static Dictionary<int, List<T>> ToGroupedDictionary<T>
(this IList<T> pList, int pGroupSize)
{
var dict = new Dictionary<int, List<T>>();
int x = 0;
while (x < pList.Count)
{
var newList = new List<T>();
for (int y = 0; y < pGroupSize && x < pList.Count; y++, x++)
newList.Add(pList[x]);
dict.Add(dict.Count, newList);
}
return dict;
}
And then you can do this:
var groups = new[]
{
"Item1",
"Item2",
"Item3",
"Item4",
"Item5",
"Item6",
"Item7",
"Item8"
}.ToGroupedDictionary(3);

LINQ: Evaluating a function where array elements are matched by index

Say I have a function c[i] = f(x[i], y[i]). I have the input value arrays x and y of equal length and I want to calculate the values of array c in the end. How do I get that using LINQ?
i.e. without having to write:
c = new double[x.Length];
for (int i = 0; i < x.Length; i++)
{
c[i] = f(x[i], y[i]);
}
Use Zip method, e.g.:
int[] x = { 1, 2, 3 };
int[] y = { 4, 5, 6 };
var result = x.Zip(y, (i, j) => i + j);
or if you have already method with adequate params, simply use:
var result = x.Zip(y, Function);
// in this sample Function looks like:
static int Function(int x, int y)
{
return x + y;
}
You can use a simple Select:
var c = x.Select((x_i, i) => f(x_i, y[i]));
If c needs to be an int[] instead of IEnumerable<int>, append ToArray to the end:
var c = x.Select((x_i, i) => f(x_i, y[i])).ToArray();
You can use the Zip method to calculate
int[] a = { 4, 2, 3 };
int[] b = { 9, 1, 0 };
var result = a.Zip(b, (i, j) => i + j);

Categories

Resources