Is there a quick and nice way using linq?
How about:
var most = list.GroupBy(i=>i).OrderByDescending(grp=>grp.Count())
.Select(grp=>grp.Key).First();
or in query syntax:
var most = (from i in list
group i by i into grp
orderby grp.Count() descending
select grp.Key).First();
Of course, if you will use this repeatedly, you could add an extension method:
public static T MostCommon<T>(this IEnumerable<T> list)
{
return ... // previous code
}
Then you can use:
var most = list.MostCommon();
Not sure about the lambda expressions, but I would
Sort the list [O(n log n)]
Scan the list [O(n)] finding the longest run-length.
Scan it again [O(n)] reporting each number having that run-length.
This is because there could be more than one most-occurring number.
Taken from my answer here:
public static IEnumerable<T> Mode<T>(this IEnumerable<T> input)
{
var dict = input.ToLookup(x => x);
if (dict.Count == 0)
return Enumerable.Empty<T>();
var maxCount = dict.Max(x => x.Count());
return dict.Where(x => x.Count() == maxCount).Select(x => x.Key);
}
var modes = { }.Mode().ToArray(); //returns { }
var modes = { 1, 2, 3 }.Mode().ToArray(); //returns { 1, 2, 3 }
var modes = { 1, 1, 2, 3 }.Mode().ToArray(); //returns { 1 }
var modes = { 1, 2, 3, 1, 2 }.Mode().ToArray(); //returns { 1, 2 }
I went for a performance test between the above approach and David B's TakeWhile.
source = { }, iterations = 1000000
mine - 300 ms, David's - 930 ms
source = { 1 }, iterations = 1000000
mine - 1070 ms, David's - 1560 ms
source = 100+ ints with 2 duplicates, iterations = 10000
mine - 300 ms, David's - 500 ms
source = 10000 random ints with about 100+ duplicates, iterations = 1000
mine - 1280 ms, David's - 1400 ms
Here is another answer, which seems to be fast. I think Nawfal's answer is generally faster but this might shade it on long sequences.
public static IEnumerable<T> Mode<T>(
this IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
var counts = source.GroupBy(t => t, comparer)
.Select(g => new { g.Key, Count = g.Count() })
.ToList();
if (counts.Count == 0)
{
return Enumerable.Empty<T>();
}
var maxes = new List<int>(5);
int maxCount = 1;
for (var i = 0; i < counts.Count; i++)
{
if (counts[i].Count < maxCount)
{
continue;
}
if (counts[i].Count > maxCount)
{
maxes.Clear();
maxCount = counts[i].Count;
}
maxes.Add(i);
}
return maxes.Select(i => counts[i].Key);
}
Someone asked for a solution where there's ties. Here's a stab at that:
int indicator = 0
var result =
list.GroupBy(i => i)
.Select(g => new {i = g.Key, count = g.Count()}
.OrderByDescending(x => x.count)
.TakeWhile(x =>
{
if (x.count == indicator || indicator == 0)
{
indicator = x.count;
return true;
}
return false;
})
.Select(x => x.i);
Here's a solution I've written for when there are multiple most common elements.
public static List<T> MostCommonP<T>(this IEnumerable<T> list)
{
return list.GroupBy(element => element)
.GroupBy(group => group.Count())
.MaxBy(groups => groups.Key)
.Select(group => group.Key)
.ToList();
}
Related
Given 2 datasets (which are both a sequence of standard deviations away from a number, we are looking for the overlapping sections):
var list1 = new decimal[] { 357.06, 366.88, 376.70, 386.52, 406.15 };
var list2 = new decimal[] { 370.51, 375.62, 380.72, 385.82, 390.93 };
I would like to perform a merge with items from List2 being placed closest to items of List1, within a certain range, i.e. merge List2 element within 5.10 (standard deviation) of List1 element:
357.06
366.88 => 370.51
376.70 => 375.52, 380.72
386.52 => 390.93
406.15
The idea is to cluster values from List2 and count them, in this case element with value 376.70 would have the highest significance as it has 2 close neighbors of 375.52 and 380.72 (where as 366.88 and 386.52 have only 1 match, and the remaining none within range).
Which C# math/stats libraries could be used for this (or would there be a better way to combine statistically)?
If this is more of a computer science or stats question apologies in advance will close and reopen on relevant SO site.
Assuming that list2 is sorted (if not, put Array.Sort(list2);) you can try Binary Search:
Given:
var list1 = new decimal[] { 357.06m, 366.88m, 376.70m, 386.52m, 406.15m };
var list2 = new decimal[] { 370.51m, 375.62m, 380.72m, 385.82m, 390.93m };
decimal sd = 5.10m;
Code:
// Array.Sort(list2); // Uncomment, if list2 is not sorted
List<(decimal value, decimal[] list)> result = new List<(decimal value, decimal[] list)>();
foreach (decimal value in list1) {
int leftIndex = Array.BinarySearch<decimal>(list2, value - sd);
if (leftIndex < 0)
leftIndex = -leftIndex - 1;
else // edge case
for (; leftIndex >= 1 && list1[leftIndex - 1] == value - sd; --leftIndex) ;
int rightIndex = Array.BinarySearch<decimal>(list2, value + sd);
if (rightIndex < 0)
rightIndex = -rightIndex - 1;
else // edge case
for (; rightIndex < list1.Length - 1 && list1[rightIndex + 1] == value + sd; ++rightIndex) ;
result.Add((value, list2.Skip(leftIndex).Take(rightIndex - leftIndex).ToArray()));
}
Let's have a look:
string report = string.Join(Environment.NewLine, result
.Select(item => $"{item.value} => [{string.Join(", ", item.list)}]"));
Console.Write(report);
Outcome:
357.06 => []
366.88 => [370.51]
376.70 => [375.62, 380.72]
386.52 => [385.82, 390.93]
406.15 => []
Something like this should work
var list1 = new double[] { 357.06, 366.88, 376.70, 386.52, 406.15 };
var list2 = new double[] { 370.51, 375.62, 380.72, 385.82, 390.93 };
double dev = 5.1;
var result = new Dictionary<double, List<double>>();
foreach (var l in list2) {
var diffs = list1.Select(r => new { diff = Math.Abs(r - l), r })
.Where(d => d.diff <= dev)
.MinBy(r => r.diff)
.FirstOrDefault();
if (diffs == null) {
continue;
}
List<double> list;
if (! result.TryGetValue(diffs.r, out list)) {
list = new List<double>();
result.Add(diffs.r, list);
}
list.Add(l);
}
It uses MinBy from MoreLinq, but it is easy to modify to work without it.
In fact, you don't need extra libs or something else. You can use just LINQ for this.
internal class Program
{
private static void Main(string[] args)
{
var deviation = 5.1M;
var list1 = new decimal[] { 357.06M, 366.88M, 376.70M, 386.52M, 406.15M };
var list2 = new decimal[] { 370.51M, 375.62M, 380.72M, 385.82M, 390.93M };
var result = GetDistribution(list1.ToList(), list2.ToList(), deviation);
result.ForEach(x => Console.WriteLine($"{x.BaseValue} => {string.Join(", ", x.Destribution)} [{x.Weight}]"));
Console.ReadLine();
}
private static List<Distribution> GetDistribution(List<decimal> baseList, List<decimal> distrebutedList, decimal deviation)
{
return baseList.Select(x =>
new Distribution
{
BaseValue = x,
Destribution = distrebutedList.Where(y => x - deviation < y && y < x + deviation).ToList()
}).ToList();
}
}
internal class Distribution
{
public decimal BaseValue { get; set; }
public List<decimal> Destribution { get; set; }
public int Weight => Destribution.Count;
}
I hope it was useful for you.
I am trying to find the nearest integer to a set integer from an array but want to pick a random one if there is more than one.
var nearestScore = scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).First();
I don't know any other way but using First().
I have also tried
var r = new random()
var nearestScore = scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).ToArray();
return nearestScore[r.next(0, nearestScore.Length - 1)]
Thanks.
You could use GroupBy() for this. It's a little heavy-handed but simple for small arrays.
Random rnd = new Random();
var nearestScore = scoreArray
.Select(x => new { Num = x, Delta = Math.Abs((long)x - realScore)) })
.OrderBy(a => a.Delta)
.GroupBy(a => a.Delta)
.First()
.OrderBy(a => rnd.Next())
.Select(a => a.Num)
.First();
Brake it in few statements:
var minScore = scoreArray.Min(x => Math.Abs((long)x - realScore));
var list = scoreArray.Where(x => Math.Abs((long)x - realScore) == minScore).ToList();
// or you can use GroupBy with OrderBy and First instead of above, but this is lighter and cleaner
r = new Random();
var myRandomNumber = r.Next(0, list.Count);
return list[myRandomNumber];
You can add an Extension FirstEqual:
public static IEnumerable<T> FirstEqual<T>(this IEnumerable<T> source)
{
var e = source.GetEnumerator();
if (e.MoveNext())
{
T first = e.Current;
yield return first;
while (e.MoveNext())
{
if (e.Current.Equals(first))
yield return first;
else
break;
}
}
}
and than you call
scoreArray.OrderBy(x => Math.Abs((long)x - realScore)).FirstEqual()
This will return you a collection of equal numbers.
We could also return a Tuple of the first number and its Count.
I have a list of values. lets say they're {1,2,3,4,5}
and I want to find the combination that sums closest to a given value
so for example if i entered 8 then the function could return
either {3,5} or {1,3,4}. either of those would be exact and i would just take the one with the least indexes 3+5
if there isn't a value that is exact for example 9.45 it would return the value
closest without going over the threshold {4,5}
I'm not sure where i would even start with this. i think it may be possible with a linq query...
static IEnumerable<int> GetClosestValues(IReadOnlyList<int> c, int t)
{
var s=0m;
return c.Select((x, i) => i).OrderByDescending(i => c[i]).Where(i => (s += c[i]) <= t).OrderBy(i => i);
}
Seems to work... not optimized though
The key is just to find all permutations of the set, and then filter (less or equal than threshold value) and order (by distance to threshold value, then by size of the set) them via LINQ:
var source = Enumerable.Range(1, 5).Select(x => (double)x).ToArray();
var permutations = Enumerable.Range(1, source.Length)
.SelectMany(x => Utils.GetOrderedPermutations(source, x))
.Dump();
var threshold = 9.45;
var result = permutations
.Select(x => x.ToArray())
.Select(x => new { Set = x, Sum = x.Sum() })
.Where(x => x.Sum <= threshold)
.OrderBy(x => threshold - x.Sum)
.ThenBy(x => x.Set.Length)
.FirstOrDefault()
.Dump();
GetOrderedPermutations is taken from this answer:
public class Utils
{
public static IEnumerable<T> Yield<T>(T value)
{
yield return value;
}
public static IEnumerable<IEnumerable<T>> GetOrderedPermutations<T>(IEnumerable<T> source, int k)
{
if (k == 0) return new[] { Enumerable.Empty<T>() };
int length = source.Count();
if (k == length) return new[] { source };
if (k > length) return Enumerable.Empty<IEnumerable<T>>();
return GetOrderedHelper<T>(source, k, length);
}
private static IEnumerable<IEnumerable<T>> GetOrderedHelper<T>(IEnumerable<T> source, int k, int length)
{
if (k == 0)
{
yield return Enumerable.Empty<T>();
yield break;
}
int i = 0;
foreach (var item in source)
{
if (i + k > length) yield break;
var permutations = GetOrderedHelper<T>(source.Skip(i + 1), k - 1, length - i);
i++;
foreach (var subPerm in permutations)
{
yield return Yield(item).Concat(subPerm);
}
}
}
}
If you can find a better title, please edit.
I will start by saying I've looked at several q&a's on this topic, mainly this one and this article without having found a way to do this:
Given the word "HALLOWEEN" I would like to find all permutations and combinations for all lengths. The first thing I tried was iterating through the below code giving it length of 1 to begin with and continuing until reaching the length of the word (9).
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] {t});
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] {t2}));
}
This gave me unexpected results as the double 'E' and 'L's were omitted, leaving the final set short.
A simpler example could be 'MOM' {M,O,M} where the final set of outcomes would be:
M
O
MO
OM
MM
MOM
MMO
OMM
Notice that I want to see both 'M's as available, but I don't want to see "MMM" as a result. "MOM" would appear twice in the result due to leaving original order (1,2,3) and swapping positions 1 and 3 (3,2,1) would both result in 'M','O','M' but this character sequence only appears once is the result list (which can be done by a string comparison)
Again, with set {1,1,2,3} I would expect to see:
{1,1}
but NOT {2,2} or {3,3}
Here's another solution that should be clear and easily understandable:
public static IEnumerable<string> GetPermutations(string input)
{
if (string.IsNullOrEmpty(input))
{
return new List<string>();
}
var length = input.Length;
var indices = Enumerable.Range(0, length).ToList();
var permutationsOfIndices = GetNumericalPermutations(indices, length);
var permutationsOfInput = permutationsOfIndices.Select(x => new string(x.Select(y => input[y]).ToArray()))
.Distinct();
return permutationsOfInput;
}
private static List<List<int>> GetNumericalPermutations(List<int> values, int maxLength)
{
if (maxLength == 1)
{
return values.Select(x => new List<int>{x}).ToList();
}
else
{
var permutations = GetNumericalPermutations(values, maxLength - 1);
foreach (var index in values)
{
var newPermutations = permutations.Where(x => !x.Contains(index))
.Select(x => x.Concat(new List<int> { index }))
.Where(x => !permutations.Any(y => y.SequenceEqual(x)))
.Select(x => x.ToList())
.ToList();
permutations.AddRange(newPermutations);
}
return permutations;
}
}
For example, the output for "MOM" is:
M
O
OM
MM
MO
MMO
OMM
MOM
I suggest looking at the permutations of the letter positions 0,1,2,3,4,etc mapping those to letters, and then eliminating the duplicates.
Without changing the GetPermutations function, I added another function to get the permutations of the letter positions, map those result to character strings and then eliminate the duplicates.
public void PermutationsTestMethod()
{
GetPermutationsOfString("MOM").ForEach(v => Debug.Print(v));
}
public List<string> GetPermutationsOfString(string value)
{
var resultList = new List<string>();
for (var i = 1; i <= value.Length; i++)
{
var permutations = GetPermutations(Enumerable.Range(0, value.Length), i);
resultList.AddRange(
permutations
.Select(v => new string(v.Select(z => value[z]).ToArray()))
.Distinct()
);
}
return resultList;
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
This works fine:
Func<string, IEnumerable<string>> getAllSubsets = null;
getAllSubsets = x =>
(x == null || !x.Any())
? Enumerable.Empty<string>()
: (x.Length > 1
? getAllSubsets(x.Substring(1))
.SelectMany(y => new [] { y, x.Substring(0, 1) + y })
: new [] { "", x.Substring(0, 1) });
So given getAllSubsets("ABC") I get:
"", "A", "B", "AB", "C", "AC", "BC", "ABC"
And, for your "MOM" example I get:
"", "M", "O", "MO", "M", "MM", "OM", "MOM"
It would be trivial to filter out the empty string and duplicate values if need be, but as it stands it strictly produces all subsets.
I think it is generally better try to avoid generating and eliminating permutations. Text like "aaaaaaaaaaaaaaab" can generate really big amount of duplications.
public static IEnumerable<IEnumerable<T>>
GetPermutationsInner<T>(IEnumerable<IGrouping<T, T>> groupedList, int length)
{
if (length == 1) return groupedList.Select(t => new T[] { t.Key });
return GetPermutationsInner<T>(groupedList, length - 1)
.SelectMany(t => groupedList
.Where(e => t.Count(w => w.Equals(e.Key)) < e.Count())
.Select(s => s.Key),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list)
{
var resultList = new List<IEnumerable<T>>();
for (int i = 1; i <= list.Count(); ++i)
{
resultList.AddRange(GetPermutationsInner<T>(list.GroupBy(g => g), i));
}
return resultList;
}
I have a list of integers from 1 to 20. I want the indices of items which are greater than 10 using linq. Is it possible to do with linq?
Thanks in advance
Use the overload of Select which includes the index:
var highIndexes = list.Select((value, index) => new { value, index })
.Where(z => z.value > 10)
.Select(z => z.index);
The steps in turn:
Project the sequence of values into a sequence of value/index pairs
Filter to only include pairs where the value is greater than 10
Project the result to a sequence of indexes
public static List<int> FindIndexAll(this List<int> src, Predicate<int> value)
{
List<int> res = new List<int>();
var idx = src.FindIndex(x=>x>10);
if (idx!=-1) {
res.Add(idx);
while (true)
{
idx = src.FindIndex(idx+1, x => x > 10);
if (idx == -1)
break;
res.Add(idx);
}
}
return res;
}
Usage
List<int> test= new List<int>() {1,10,5,2334,34,45,4,4,11};
var t = test.FindIndexAll(x => x > 10);