I need to find most common digit in array of ints, i would also like to have the highest index(number) of them, so if there is input like [11, 22, 33] then it will return 3 instead of 1. How can I achieve that in easy way?
static uint mostCommonDigit(uint[] n)
{
uint[] numbersFrequency = new uint[10];
foreach(uint i in n)
{
uint a = i;
if (a != 0)
{
while (a>0)
{
uint d = a % 10;
a = a / 10;
numbersFrequency[d] += 1;
}
}
}
uint max = numbersFrequency.Max();
int index = Array.IndexOf(numbersFrequency, max);
return (uint)index;
}
You can use this linq to get your position:
List<int> iii = new List<int> { 11, 22, 33 };
int yyy2 = iii.IndexOf(iii.Last(y => y.ToString().GroupBy(c => c).Select(c => c.Count()).Max() == iii.Select(x => x.ToString().GroupBy(c => c).Select(c => c.Count()).Max()).Max())) + 1;
You could convert each element of the list to a string and concatenate them. Then, you could count the occurences of each character in that string. By sorting by character count and then by character value, higher characters will be sorted first if they occur with the same frequency:
char MostCommonDigit(int[] list)
{
return list.Aggregate("", (i, j) => $"{i}{j}")
.GroupBy(c => c)
.Select(
g => new {
Char = g.Key,
Count = g.Count()
})
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.Char)
.First().Char;
}
So
Console.WriteLine(MostCommonDigit(new [] { 11, 22, 33 }));
Console.WriteLine(MostCommonDigit(new [] { 111, 22, 33 }));
prints
3
1
As far as "easy" way, here is another LINQ alternative :
static uint mostCommonDigit(uint[] n) =>
(uint)string.Concat(n).GroupBy(c => c).Max(g => (g.Count(), g.Key - '0')).Key
string.Concat converts the array to string (for example "112233").
GroupBy groups the characters in the string by character (for example '1' => ['1', '1'], '2' => ['2', '2']).
The Max part is similar to ordering by the number of items in each group, then by the key of each group, and then getting the last item, but it avoids the sorting. The - '0' part converts the character key to integer.
It is probably few times slower than your solution due to the overhead from LINQ, but the difference will be in milliseconds and not noticable for such small arrays.
Related
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();
We have a program that shows you how many times a letter is repeated in a text
string txt = input.text.ToLower();
txt = Regex.Replace(txt, #"\s+", "").Replace(")","").Replace("(","").Replace(".","").Replace(",","").Replace("!","").Replace("?","") ;
var letterCount = txt.Where(char.IsLetter).GroupBy(c => c).Select(v => new { Letter = v.Key, count = v.Count() });
foreach (var c in letterCount)
{
Debug.Log(string.Format("Caracterul:{0} apare {1} ori", c.Letter.ToString(), c.count));
}
And how do I give for the most repeating letter the value of 26, then for the one that repeats the less it gets 25 and for the one that only once a value in alphabetical order?
For example, the text "we are all happy"
Letter A is repeated three times and has the value of 26
For letter L 25
For P 24 and others in alphabetical order
And, finally, get their sum?
Sorry for my English!!!
You can use this LINQ approach:
string input = "we are all happy";
var allCharValues = input.ToLookup(c => c)
.Where(g => g.Key != ' ') // or you want spaces?
.OrderByDescending(g => g.Count())
.ThenBy(g => g.Key) // you mentioned alphabetical ordering if two have same count
.Select((x, index) => new { Char = x.Key, Value = 26 - index, Count = x.Count() });
foreach (var x in allCharValues)
Console.WriteLine($"Char:{x.Char} Value:{x.Value} Count:{x.Count}");
int sum = allCharValues.Select(x => x.Value).Sum();
In relation to your question about removing unwanted characters:
I think you'd be better of just keeping all characters between a and z. You could write an extension method to do this, and convert to lowercase at the same time:
public static class StringExt
{
public static string AlphabeticChars(this string self)
{
var alphabeticChars = self.Select(char.ToLower).Where(c => 'a' <= c && c <= 'z');
return new string(alphabeticChars.ToArray());
}
}
Then you can use an approach as follows. This is similar to Tim's approach, but this uses GroupBy() to count the occurrences; it also uses the new Tuple syntax from C#7 to simplify things. Note that this ALSO names the tuple properties, so they are not using the default Item1 and Item2.
string txt = "we, (are?) all! happy.";
var r = txt
.AlphabeticChars()
.GroupBy(c => c)
.Select(g => (Count: g.Count(), Char: g.Key))
.OrderByDescending(x => x.Count)
.ThenBy(x => x.Char)
.Select((v, i) => (Occurance: v, Index: 26-i));
int sum = r.Sum(c => c.Occurance.Count * c.Index);
Console.WriteLine(sum);
I need to sort the array from minimum to maximum value, but I need to return only the index of array after sorting it. I dont want to swap values, I just need to return the values index according to the value size,
for eg
int[] arr = {7,8,2,3,1,5};
for (int i=0; i<=arr.length; i++)
{
int index = Array.IndexOf(arr, i);
}
Now I want to return index of values from minimum to maximum
as 4,2,3,5,0,1.
Your check in the for loop is wrong it should be i < arr.Length. For index you can do:
int[] arr = { 7, 8, 2, 3, 1, 5 };
int[] sortedIndexArray = arr.Select((r, i) => new { Value = r, Index = i })
.OrderBy(t => t.Value)
.Select(p => p.Index)
.ToArray();
For output:
foreach(int item in sortedIndexArray)
Console.WriteLine(item);
Output:
4
2
3
5
0
1
var indexes = arr.Select((i, inx) => new { i, inx })
.OrderBy(x => x.i)
.Select(x => x.inx)
.ToArray();
This question already has answers here:
Find character with most occurrences in string?
(12 answers)
Closed 7 years ago.
I want to find the Mode in an Array. I know that I have to do nested loops to check each value and see how often the element in the array appears. Then I have to count the number of times the second element appears. The code below doesn't work, can anyone help me please.
for (int i = 0; i < x.length; i ++)
{
x[i]++;
int high = 0;
for (int i = 0; i < x.length; i++)
{
if (x[i] > high)
high = x[i];
}
}
Using nested loops is not a good way to solve this problem. It will have a run time of O(n^2) - much worse than the optimal O(n).
You can do it with LINQ by grouping identical values and then finding the group with the largest count:
int mode = x.GroupBy(v => v)
.OrderByDescending(g => g.Count())
.First()
.Key;
This is both simpler and faster. But note that (unlike LINQ to SQL) LINQ to Objects currently doesn't optimize the OrderByDescending when only the first result is needed. It fully sorts the entire result set which is an O(n log n) operation.
You might want this O(n) algorithm instead. It first iterates once through the groups to find the maximum count, and then once more to find the first corresponding key for that count:
var groups = x.GroupBy(v => v);
int maxCount = groups.Max(g => g.Count());
int mode = groups.First(g => g.Count() == maxCount).Key;
You could also use the MaxBy extension from MoreLINQ method to further improve the solution so that it only requires iterating through all elements once.
A non LINQ solution:
int[] x = new int[] { 1, 2, 1, 2, 4, 3, 2 };
Dictionary<int, int> counts = new Dictionary<int, int>();
foreach( int a in x ) {
if ( counts.ContainsKey(a) )
counts[a] = counts[a]+1
else
counts[a] = 1
}
int result = int.MinValue;
int max = int.MinValue;
foreach (int key in counts.Keys) {
if (counts[key] > max) {
max = counts[key];
result = key;
}
}
Console.WriteLine("The mode is: " + result);
As a beginner, this might not make too much sense, but it's worth providing a LINQ based solution.
x
.GroupBy(i => i) //place all identical values into groups
.OrderByDescending(g => g.Count()) //order groups by the size of the group desc
.Select(g => g.Key) //key of the group is representative of items in the group
.First() //first in the list is the most frequent (modal) value
Say, x array has items as below:
int[] x = { 1, 2, 6, 2, 3, 8, 2, 2, 3, 4, 5, 6, 4, 4, 4, 5, 39, 4, 5 };
a. Getting highest value:
int high = x.OrderByDescending(n => n).First();
b. Getting modal:
int mode = x.GroupBy(i => i) //Grouping same items
.OrderByDescending(g => g.Count()) //now getting frequency of a value
.Select(g => g.Key) //selecting key of the group
.FirstOrDefault(); //Finally, taking the most frequent value
A basic solution would look like this:
bool sortTest(int[] numbers, int target)
{
Array.Sort(numbers);
for(int i = 0; i < numbers.Length; i++)
{
for(int j = numbers.Length-1; j > i; j--)
{
if(numbers[i] + numbers[j] == target)
return true;
}
}
return false;
}
Now I'm very new to LINQ but this is what I have written so far:
var result = from num in numbers
where numbers.Contains(target -num)
select num;
if (result.Count() > 0)
return true;
return false;
Now i'm running into an issue given the following example:
Array: 1, 2, 4, 5, 8
Target: 16
It should return back false, but it's catching 16-8=8. So how do I go about not letting it notice itself in the contains check? Or can I make a second array each time within the query that doesn't contain the number I'm working with(thus solving the problem)?
Thanks in advance.
Is this what you're looking for?
var result = from n1 in numbers
from n2 in numbers
where n1 != n2 && n1 + n2 == target
select new { n1, n2 };
[Edit]
This returns matches twice and ignores the situation where a number is duplicated in the array. You can't handle these situations using Expression Syntax because you can't access the index of a matched item, but you can do it like this:
var result = numbers.Select((n1, idx) =>
new {n1, n2 = numbers.Take(idx).FirstOrDefault(
n2 => n1 + n2 == target)}).Where(pair => pair.n2 != 0);
As long as you don't have any zeros in your array.
[Further thought Edit]
The perfect mix solution:
var result = from item in numbers.Select((n1, idx) =>
new {n1, shortList = numbers.Take(idx)})
from n2 in item.shortList
where item.n1 + n2 == target
select new {n1 = item.n1, n2};
What I'd do to solve this problem in general is first write a "chooser".
public static IEnumerable<IEnumerable<T>> Chooser<T>(this IList<T> sequence, int num)
{ ... left as an exercise ... }
The output of the chooser is a sequence of sequences. Each sub-sequence is of length num, and consists of elements chosen from the original sequence. So if you passed { 10, 30, 20, 50 } as the sequence and 3 for num, you'd get the sequence of sequences:
{10, 30, 20}, {10, 30, 50}, {10, 20, 50}, {30, 20, 50}
as a result.
Once you've written Chooser, the problem becomes easy:
var results =
from subsequence in numbers.Chooser(2)
where subsequence.Sum() == target
select subsequence;
And now you can solve the problem for subsequences of other sizes, not just pairs.
Writing Chooser is a bit tricky but it's not too hard.
To improve on pdr's reply and address the concerns mentioned in the comments you could use the overloaded Select method to compare the indices of the items and ensure uniqueness.
public bool sortTest(int[] numbers, int target)
{
var indexedInput = numbers.Select((n, i) => new { Number = n, Index = i });
var result = from x in indexedInput
from y in indexedInput
where x.Index != y.Index
select x.Number + y.Number == target;
return result.Any(item => item);
}
Or in dot notation:
var result = numbers.Select((n, i) => new { Number = n, Index = i })
.SelectMany(
x => indexedInput,
(x, y) => new { x = x, y = y })
.Where(item => item.x.Index != item.y.Index)
.Select(item => item.x.Number + item.y.Number == target);