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();
Related
I have an unknown number of buckets(collections), and each bucket having an unknown number of entities
I need to produce a cartesian product of all the entities, so that I endup with a single COLLECTION that has ARRAYS of entities and in each array, there is 1 representetive from EVERY bucket.
So that if I have 5 buckets (B1..B5), and buckets B1, B2 have 1 item each, and bucket B3, B4 and B5 have 4, 8 and 10 items each, I'll have a collection of 320 arrays, and each array will have 5 items.
The only stupud issue here, is that both size of buckets and number of buckets is unknown at development time.
Performance is not super important here, as most of the time, my buckets will have only 1 entity, and only rarely will there be times when some of my buckets will contain 20-30 items...and I'll usually have 5-30 buckets
I'd love to utilize linq here in someway, but my brain is getting fried as I try to imagine how this would work
You could create an extension method like the following:
public static class EnumerableExtensions
{
public static IEnumerable<TValue []> Permutations<TKey, TValue>(this IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> selector)
{
var keyArray = keys.ToArray();
if (keyArray.Length < 1)
yield break;
TValue [] values = new TValue[keyArray.Length];
foreach (var array in Permutations(keyArray, 0, selector, values))
yield return array;
}
static IEnumerable<TValue []> Permutations<TKey, TValue>(TKey [] keys, int index, Func<TKey, IEnumerable<TValue>> selector, TValue [] values)
{
Debug.Assert(keys.Length == values.Length);
var key = keys[index];
foreach (var value in selector(key))
{
values[index] = value;
if (index < keys.Length - 1)
{
foreach (var array in Permutations(keys, index+1, selector, values))
yield return array;
}
else
{
yield return values.ToArray(); // Clone the array;
}
}
}
}
As an example, it could be used like:
public static void TestPermutations()
{
int [][] seqence = new int [][]
{
new int [] {1, 2, 3},
new int [] {101},
new int [] {201},
new int [] {301, 302, 303},
};
foreach (var array in seqence.Permutations(a => a))
{
Debug.WriteLine(array.Aggregate(new StringBuilder(), (sb, i) => { if (sb.Length > 0) sb.Append(","); sb.Append(i); return sb; }));
}
}
and produce the following output:
1,101,201,301
1,101,201,302
1,101,201,303
2,101,201,301
2,101,201,302
2,101,201,303
3,101,201,301
3,101,201,302
3,101,201,303
Is that what you want?
Here's how to do it without recursion in a single Linq statement (wrapped around an extension method for convenience):
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(
IEnumerable<IEnumerable<T>> listOfLists)
{
return listOfLists.Skip(1)
.Aggregate(listOfLists.First()
.Select(c => new List<T>() { c }),
(previous, next) => previous
.SelectMany(p => next.Select(d => new List<T>(p) { d })));
}
The idea is simple:
Skip the first row, so we can use it as the initial value of an aggregate.
Place this initial value in a list that we'll grow on each iteration.
On each iteration, create a new list for each element in previous and add to it each of the elements in next (this is done by new List<T>(p) { d }).
EXAMPLE
Suppose you have an array of arrays as follows:
var arr = new[] {
new[] { 1,2 },
new[] { 10,11,12 },
new[] { 100,101 }
};
Then arr.GetPermutations() will return a list of lists containing:
1,10,100
1,10,101
1,11,100
1,11,101
1,12,100
1,12,101
2,10,100
2,10,101
2,11,100
2,11,101
2,12,100
2,12,101
Non-Linq, non-recursive solution that's faster. We pre-allocate the entire output matrix and then just fill it in a column at a time.
T[][] Permutations<T>(T[][] vals)
{
int numCols = vals.Length;
int numRows = vals.Aggregate(1, (a, b) => a * b.Length);
var results = Enumerable.Range(0, numRows)
.Select(c => new T[numCols])
.ToArray();
int repeatFactor = 1;
for (int c = 0; c < numCols; c++)
{
for (int r = 0; r < numRows; r++)
results[r][c] = vals[c][r / repeatFactor % vals[c].Length];
repeatFactor *= vals[c].Length;
}
return results;
}
Another LINQ-based option, based on suggestion from Diego, but more precise in terms of argument and return types.
It also does not require multiple enumerations of outer collection and hence does not produce Resharper's hint "Possible multiple enumerations".
public static IEnumerable<IReadOnlyCollection<T>> GetPermutations<T>(
IEnumerable<IReadOnlyCollection<T>> collections) =>
collections
.Aggregate(
new[] { Array.Empty<T>() },
(acc, next) =>
acc
.SelectMany(accItem =>
next.Select(nextItem => accItem.Concat(new[] { nextItem }).ToArray()))
.ToArray());
This is probably a very late answer, but I encounter a similar problem i.e. generate all permutations of a list of list of string. However, in my problem, I don't need all permutations simultaneously. I only need/generate next permutation if current permutation doesn't satisfy my condition. Therefore, the following is my way of doing a kind of "for each" and with conditional continuation during permutations generation. This answer is inpsired by Tom19's answer.
void ForEachPermutationDo<T>(IEnumerable<IEnumerable<T>> listOfList, Func<IEnumerable<T>, bool> whatToDo) {
var numCols = listOfList.Count();
var numRows = listOfList.Aggregate(1, (a, b) => a * b.Count());
var continueGenerating = true;
var permutation = new List<T>();
for (var r = 0; r < numRows; r++) {
var repeatFactor = 1;
for (var c = 0; c < numCols; c++) {
var aList = listOfList.ElementAt(c);
permutation.Add(aList.ElementAt((r / repeatFactor) % aList.Count()));
repeatFactor *= aList.Count();
}
continueGenerating = whatToDo(permutation.ToList()); // send duplicate
if (!continueGenerating) break;
permutation.Clear();
}
}
Using the above method, generating all permutation can be done like
IEnumerable<IEnumerable<T>> GenerateAllPermutations<T>(IEnumerable<IEnumerable<T>> listOfList) {
var results = new List<List<T>>();
ForEachPermutationDo(listOfList, (permutation) => {
results.Add(permutation);
return true;
});
return results;
}
Given an array and two more arrays i need to find the range of elements in the first array
For e.g. MainArray={2,4,6,5,8,9}, range1={4,5,6}, range2={6,9,8}
for First-Iteration i have to select elements in MainArray in range [4,6] ->[4,6,5] --[3] is the output
for second-Iteration i have to select elements in MainArray in range [5,9] ->[5,8,9]--[3] is the output
for third-Iteration i have to select elements in MainArray in range [6,8] ->[6,8]--[2] is the output
array returned [3,3,2]
static void Main(string[] args)
{
var rng = new Random();
var result = processFunc(Enumerable.Range(0, 5000000).OrderBy(x => rng.Next()).ToArray(),
Enumerable.Range(0, 20000).OrderBy(x => rng.Next()).Take(200).ToArray(),
Enumerable.Range(0, 20000).OrderBy(x => rng.Next()).Take(200).ToArray());
}
public static int[] processFunc(int[] scores,int[] l,int[] r)
{
IList<int> output = new List<int>();
for (int i = 0; i < l.Length; i++)
{
var bestMatch = scores.Where(x => x >= l[i] && x <= r[i]);
output.Add(bestMatch.Count());
}
return output.ToArray();
}
The code runs fine when numbers are small but once they >50,000 the program becomes slow. How can I optimize this solution ?
Assuming l and r have the same length, consider this approach:
public static int[] processFunc(int[] scores, int[] l, int[] r)
{
var min = Math.Min(l.Min(z => z), r.Min(z => z));
var max = Math.Max(l.Max(z => z), r.Max(z => z));
var grouped = scores.Where(z => z >= min && z <= max).GroupBy(z => z).Select(val => Tuple.Create(val.Key, val.Count())).OrderBy(z => z.Item1).ToList();
return l.Zip(r, (left, right) =>
{
var matching = grouped.Where(z => z.Item1 >= left).TakeWhile(z => z.Item1 <= right);
return matching.Sum(z => z.Item2);
}).ToArray();
}
min and max are used to ignore irrelevant (too large or too small) numbers. grouped is used to pre-calculate the counts and put them in order. Zip is used to line up the l and r values and sum the counts together.
This solution is roughly 2-3 times faster on my machine than the original code (and most of the remaining time being spent is actually setting up the parameters, rather than in the function itself).
Assume that I have a list of items from 1 - 3.
I could order them by 1,1,2,2,3,3.
But instead, I would like to order them by 1,2,3,1,2,3....
Is there an already exist function to achieve that?
This approach separates each number into groups, then iterates through the groups in order while conditionally adding them to a result list. There's probably ways to make this safer and more efficient, but this should give you a start. (It assumes that if there aren't equal counts of each number in the source array, it will skip those numbers as it runs out of them during the iteration phase.)
int[] arr = new[] { 1,1,1,2,2,2,3,3,3,4,4,4,5,5,5 };
var orderList = arr.OrderBy(x => x).Distinct().ToArray();
var refList = arr.GroupBy(x => x).ToDictionary(k => k.Key, v => v.Count());
var result = new List<int>();
int i = 0;
while (result.Count < arr.Length)
{
if (refList.Values.Sum() == 0)
break;
if (refList[orderList[i]] > 0)
{
result.Add(orderList[i]);
refList[orderList[i]]--;
}
i++;
if (i >= orderList.Length)
i = 0;
}
// Result: [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5]
I am working on creating calculations from a spreadsheet into C#, and I was wondering if C# has a similar method to Rank in Excel?
Rank in Excel
Returns the rank of a number in a list of numbers. The rank of a
number is its size relative to other values in a list. (If you were to
sort the list, the rank of the number would be its position.)
Syntax
RANK(number,ref,order)
Number is the number whose rank you want to find.
Ref is an array of, or a reference to, a list of numbers.
Nonnumeric values in ref are ignored.
Order is a number specifying how to rank number.
If order is 0 (zero) or omitted, Microsoft Excel ranks number as if
ref were a list sorted in descending order. If order is any nonzero
value, Microsoft Excel ranks number as if ref were a list sorted in
ascending order.
The same can be achieved through code, but I just wanted to check if there was anything I was missing first.
You can, sort of.
SortedList<int, object> list = new SortedList<int, object>();
// fill with unique ints, and then look for one
int rank = list.Keys.IndexOf(i);
Rank will be an ascending, zero-based position.
You could pretty it up by writing an extension method:
public static class Extensions
{
public static int Rank(this int[] array, int find)
{
SortedList<int, object> list = new SortedList<int, object>();
for (int i = 0; i < array.Length; i++)
{
list.Add(array[i], null);
}
if (list.ContainsKey(find))
{
return list.Keys.IndexOf(find);
}
else
{
return -1;
}
}
}
And use it like:
int[] ints = new int[] { 2, 7, 6, 3, 9, 12 };
int rank = ints.Rank(2);
...but I'm not convinced its the most sensible thing to do.
To get the equivalent of RANK you'll need to get the minimum index of each item when you group:
var ranks = list.OrderBy(x => x)
.Select((x, i) => new {x, i = i+1}) // get 1-based index of each item
.GroupBy(xi => xi.x) // group by the item
.Select(g => new {rank = g.Min(xi => xi.i), items = g}) // rank = min index of group
.SelectMany(g => g.items, (g, gg) => new {g.rank, gg.i}) ; // select rank and item
or if you'rs grouping by the property of a class:
var ranks = list.OrderBy(x => x.{some property})
.Select((x, i) => new {x, i = i+1}) // get 1-based index of each item
.GroupBy(xi => xi.x.{some property}) // group by the item's property
.Select(g => new {rank = g.Min(xi => xi.i), items = g}) // rank = min index of group
.SelectMany(g => g.items, (g, gg) => new {g.rank, gg.i}) ; // select rank and item
This works for me so far (and it is simpler)
public static int Rank<T>(T value, IEnumerable<T> data)
{
return data.OrderByDescending(x => x).ToList().IndexOf(value) + 1;
}
I used T so it can take all numeric types (int/double/decimal).
The usage is similar to Excel
int[] data = new[] { 3, 2, 2, 3, 4 };
int rank = Rank(3, data); // returns 2
I hope I didn't miss anything
ok, this should be interesting.
lets assume i have the following code:
in this example, the first available number would be 2.
List<long> myList = new List<long>(){0,1,10,3};
in this example, the first available number would be '4'.
List<long> myList = new List<long>(){0,1,2,3};
any ideas?
So by "available" you mean "the lowest non-negative number which doesn't already exist in the list"?
I'd be tempted to write something like:
HashSet<long> existing = new HashSet<long>(list);
for (long x = 0; x < long.MaxValue; x++)
{
if (!existing.Contains(x))
{
return x;
}
}
throw new InvalidOperationException("Somehow the list is enormous...");
EDIT: Alternatively, you could order the list and then find the first value where the index isn't the same as the value...
var ordered = list.OrderBy(x => x);
var differences = ordered.Select((value, index) => new { value, index })
.Where(pair => pair.value != pair.index)
.Select(pair => (int?) pair.index);
var firstDifference = differences.FirstOrDefault();
long nextAvailable = firstDifference ?? list.Count;
The last line is to take care of the situation where the list is contiguous from 0. Another alternative would be:
var nextAvailable = list.Concat(new[] { long.MaxValue })
.OrderBy(x => x)
.Select((value, index) => new { value, index })
.Where(pair => pair.value != pair.index)
.Select(pair => pair.index)
.First();
This should be fine so long as the list doesn't contain long.MaxValue + 1 elements, which it can't in current versions of .NET. (That's a lot of memory...) To be honest, this will already have problems when it goes beyond int.MaxValue elements due to the Select part taking an int index...
list.Sort();
var range = Enumerable.Range( list.First(), list.Last()- list.First());
var number = range.Except(list).FirstOrDefault();