I'm reading in a text file using StreamReader to the program. I need to record the frequency of each letter in the string into an array (where index 0 would be A, and so on). What's the simplest approach for this?
Edit: I had this originally, until I realized it was completely wrong.
int counter = 0;
int[] freq = new int[26]; // create frequency array
// counts frequency
while (counter < inValue.Length)
{
int A = 65; // ASCII value for "A"
char x = char.Parse(inValue.Substring(counter, 1)); // get individual characters from string
int s = (int)x; // cast character to integer value
if (s == A + counter)
freq[counter]++;
counter++;
}
Where inValue is the text file StreamReader reads into the program.
var freqs = File.ReadAllText("myfile.txt")
.Where(c => Char.IsLetter(c))
.GroupBy(c => c)
.ToDictionary(g => g.Key, g => g.Count());
This should give you a Dictionary of characters and their count.
Update:
If you want case insensitive counts, just change the GroupBy:
.GroupBy(c => Char.ToUpper(c)) // instead of .GroupBy(c => c)
And in my opinion a dictionary is better than an array in this case because the character that the "count" belongs to is not just implied by the index; instead, it is an explicit key. This makes lookups easier because you don't have to convert the character to an index. Additionally, this makes it more flexible when adding internationalization support. However, if you absolutely need an array, it is a simple change:
var freqs = File.ReadAllText("myfile.txt")
.Where(c => Char.IsLetter(c))
.GroupBy(c => c)
.OrderBy(g => g.Key)
.Select(g => g.Count())
.ToArray()
You can try something like this. This worked for me but I didnt used StreamReader:-
int[] c = new int[(int)char.MaxValue];
string s = File.ReadAllText("text.txt");
foreach (char t in s)
{
c[(int)t]++;
}
for (int i = 0; i < (int)char.MaxValue; i++)
{
if (c[i] > 0 &&
char.IsLetterOrDigit((char)i))
{
Console.WriteLine("Letter: {0} Frequency: {1}",(char)i, c[i]);
}
}
A few modifications to your code will make it work, assuming that you only want to count the letters 'A' through 'Z':
int counter = 0;
int[] freq = new int[26]; // create frequency array
// counts frequency
while (counter < inValue.Length)
{
char c = invalue[counter];
if (c >= 'A' && c <= 'Z')
{
++freq[(int)c - 65]
}
++counter;
}
If you want to count lower case letters as well, then change the first line in the loop to:
char c = char.ToUpper(invalue[counter]);
I spent quite a while to figure out this Linq which will result in the exact same array you want:
int[] occurance = File.ReadAllText("myfile.txt")
.Where(c => char.IsLetter(c))
.Select(c => (int)char.ToUpperInvariant(c) - 65)
.GroupBy(a => a)
.ToDictionary(a => a.Key, a => a.Count())
.OrderBy(a => a.Key)
.Select(a => a.Value)
.ToArray();
Related
As the title says i have a task to find the longest repeating sequence in a string and it has to be done with linq only - no ifs, no loop, no try, assignment is only allowed on initialization of variables, recursion is allowed. I've found the solution online and i understand what is happening but i can't transform it to linq -I'm not that familiar with it. I would greatly appreciate if someone could help me. Here is a link to what ive found -https://www.javatpoint.com/program-to-find-longest-repeating-sequence-in-a-string.
List<int> a = new List<int> {1, 2, 1, 2, 1, 2, 3, 2, 1, 2};
List<List<int>> aa = new List<List<int>>();
outerLoop(a);
var max = aa.Max(x => x.Count);
var m = from v in aa
where v.Count == max
select v;
m.Dump();
void outerLoop(List<int> list)
{
List<int> f = new List<int>();
f.AddRange(list.Skip(list.Count-1).Take(list.Count).ToList());
innerLoop(list, list.Skip(1).Take(list.Count).ToList());
f.ForEach(k => outerLoop(list.Skip(1).Take(list.Count).ToList()));
}
void innerLoop(List<int> l, List<int> subList)
{
List<int> f = new List<int>();
f.AddRange(subList.Skip(subList.Count-1).Take(subList.Count).ToList());
var tt = l.TakeWhile((ch, i) => i < subList.Count && subList[i] == ch).ToList();
aa.Add(tt);
f.ForEach(k => innerLoop(l, subList.Skip(1).Take(subList.Count).ToList()));
}
so i came up with this "beauty", i don't think it's good code but i think it works. If anyone is interested and wants to make suggestions how to make it better, they are more than welcome to :)
if input is int[] x= {1, 2, 1, 2, 1, 2, 3, 2, 1, 2}
result should be 1212
Give this a go:
List<int> words = new List<int> { 1, 2, 1, 2, 1, 2, 3, 2, 1, 2 };
string result =
words
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, words.Count - i).Select(j => words.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => String.Join(",", x.w), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me 1,2,1,2.
If you want one that actually works with strings, try this:
var word = "supercalifragilisticexpialidocious";
string result =
word
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, word.Length - i).Select(j => word.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => new string(x.w.ToArray()), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me ali.
Here's a slightly more understandable version:
var word = "supercalifragilisticexpialidocious";
string result =
(
from i in Enumerable.Range(0, word.Length)
from j in Enumerable.Range(1, word.Length - i)
group i by word.Substring(i, j) into gis
where gis.Skip(1).Any()
orderby gis.Key.Length descending
select gis.Key
).First();
Here is my version. It isn't a single LINQ expression, but does use only LINQ. It does return all same length subsequences if there are multiple answers. It should work any type of sequence. It was written to only use standard LINQ methods.
It uses GroupBy with a string key to implement a sequence Distinct. (Because of this trick, lists that contain items with commas might not work right.) In production code, I would use a Distinct with an IEqualityComparer for sequences based on SequenceEqual. It also has a separate step for finding the maximum repeated sequence length and then finding all the matching sequences, in production code I would use a MaxBy extension.
Update: Since I was using GroupBy for DistinctBy, I realized I could just use that to count the subsequence repeats directly rather than search for them.
var repeaters = Enumerable.Range(0, words.Count) // starting positions
.SelectMany(n => Enumerable.Range(1, (words.Count - n) / 2).Select(l => words.Skip(n).Take(l).ToList())) // subseqs from each starting position
.GroupBy(s => String.Join(",", s), (k, sg) => new { seq = sg.First(), Repeats = sg.Count() }) // count each sequence
.Where(sr => sr.Repeats > 1) // only keep repeated sequences
.Select(sr => sr.seq); // no longer need counts
var maxRepeaterLen = repeaters.Select(ss => ss.Count()).Max(); // find longest repeated sequence's length
var maxLenRepeaters = repeaters.Where(ss => ss.Count() == maxRepeaterLen); // return all sequences matching longest length
I have a two dimensional array containing objects of type MyObj.
private MyObj[,] myObjs = new MyObj[maxX, maxY];
I want to get the indices from the array when passing in a matching object. I want to get the x and y value from this array. I can return these two values as a Position object that takes a x and y coordinate.
private Position GetIndices(MyObj obj)
{
for (int x = 0; x < myObjs.GetLength(0); x++)
{
for (int y = 0; y < myObjs.GetLength(1); y++)
{
if (myObjs[x, y] == obj)
{
return new Position(x, y);
}
}
}
}
Is it possible to get this code shorten to some Linq code lines?
But I don't think, it looks nice :)
var result = Enumerable.Range(0, myObjs.GetLength(0))
.Select(x => Enumerable.Range(0, myObjs.GetLength(1)).Select(y => new { x, y }))
.SelectMany(o => o)
.FirstOrDefault(o => myObjs[o.x, o.y] == obj);
Here's another option, if you're interested. It uses an indexer inside the first select and does a little math to find where that index falls inside the two-dimensional array.
var o = new MyObj();
myObjs[1,2] = o;
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 / myObjs.GetLength(1), x.Item2 % myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=1,Y=2}
It sorta looks like you're considering the x-coordinate to be the height of the array, and the y-coordinate the width, in which case you'd want to switch it up slightly:
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 % myObjs.GetLength(1), x.Item2 / myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=2,Y=1}
I used Point instead of Position since it's built into .NET but you should be able to just swap one for the other.
Yes, that is possible. You can ask Resharper to do the job (loop to linq) for you. After installation, just use the feature.
I'm trying to get the letter of an array that as a max value of a repeated letters on a string.
I have is this:
var AsciiCode = new int[255];
string word= "Hello everybody";
foreach (char c in word)
{
AsciiCode[c]++;
}
MessageBox.Show(string.Format("The max count is:
{0}\nLetter: {1}", AsciiCode.Max(), AsciiCode.ElementAt(//MAX_VALUE_HERE//) ));
A solution with using Linq can be this:
var res =
word.GroupBy(g => g)
.Select(c => new { c.Key, Count = c.Count() })
.OrderByDescending(o => o.Count)
.FirstOrDefault();
C# Demo
This question already has answers here:
Split a collection into `n` parts with LINQ? [duplicate]
(21 answers)
Closed 6 years ago.
Hi everybody I have an array with 1 to 100 number and I want to group it to four group that each group have 25 number. How can i do it. Thanks
static void Main(string[] args)
{
int[] array = new int[101];
for (int i = 1; i <= 100; i++)
{
array[i] = i; Console.WriteLine(array[i]);
}
var s = array.GroupBy(x => array.Length % 25).Select(d => new { k = d.Key, v = d.OrderBy(f => f) });
foreach (var item in s)
{
Console.WriteLine($"{item.k}");
foreach (var item2 in item.v)
{
Console.WriteLine($"\t{item2}");
}
Console.WriteLine("------------");
}`enter code here`
Your question is vague; there're many ways to group by the array:
int[] array = Enumerable
.Range(1, 100)
.ToArray();
Possible groupings (by index):
int[][] result = array
.Select((item, index) => new {
item = item,
index = index })
.GroupBy(chunk => chunk.index % 4)
.Select(chunk => chunk
.Select(x => x.item)
.ToArray())
.ToArray();
Or
int[][] result = array
.Select((item, index) => new {
item = item,
index = index })
.GroupBy(chunk => chunk.index / 25)
.Select(chunk => chunk
.Select(x => x.item)
.ToArray())
.ToArray();
Or grouping by value
int[][] result = array
.GroupBy(item => item % 4)
.Select(chunk => chunk
.ToArray())
.ToArray();
To print out the result (and test grouping) use string.Join:
string report = string.Join(Environment.NewLine, result
.Select(line => string.Join(" ", line
.Select(item => string.Format("{0,3}", item)))));
Console.Write(report);
I wonder If there is any c# functions that checks if the letter exists more then once? In another word, I send a string to a function as parameter to check whether a letter exist more than once or not. For example the string "AABDCK" should return "A". Is there is any way to use dictionary??
Is there is any way to use dictionary??
Yes loop through each character in your string and track the number of occurrences of each character in a Dictionary<char, int>.
Dictionary<char, int> counts = new Dictionary<char, int>();
foreach (var ch in myString)
{
if (counts.ContainsKey(ch))
{
counts[ch]++;
}
else counts.Add(ch, 1);
}
Check the dictionary for keys where the value is > 1.
You can also do this with Linq. I'm not in front of a compiler, but it would look something like
List<char> multipleTimes = myString
.GroupBy(c => c)
.Select(g => new { Character = g.Key, Count = g.Count() })
.Where(a => a.Count > 1)
.Select(a => a.Character)
.ToList();
You could do it using linq, look the comments bellow to understand the code, for sample:
public string GetLetterWithMoreOccurrences(string text)
{
// check if the text was provided
if (string.IsNullOrEmpty(letter))
throw new ArgumentException("You must provide a text.", "text");
// if it is lower than 2 chars, return the first one
// I'm not sure if it is what you want, but let's consider it.
if (text.Length <= 2)
return text[0];
// find the first letter
var letter = text.GroupBy(c => c) // group by char
.Select(x => { Letter = x.Key, Total = x.Count() }) // in the group, count how many occurrences each letter has
.OrderByDescending(x => x.Total) // order by the total by descending
.First(); // get the first one
return letter;
}
and you can check:
var letter = GetLetterWithMoreOccurrences("AABDCK");
// should return "A"
Now, if you want all the letter that has more than one occurrences, you could try:
public string GetLetterAllDuplicates(string text)
{
// check if the text was provided
if (string.IsNullOrEmpty(letter))
throw new ArgumentException("You must provide a text.", "text");
// if it is lower than 2 chars, return the first one
// I'm not sure if it is what you want, but let's consider it.
if (text.Length <= 2)
return text[0];
// find the first letter
var letters = text.GroupBy(c => c) // group by char
// in the group, count how many occurrences each letter has
.Select(x => { Letter = x.Key, Total = x.Count() })
// get only the occurrences that has more than 1.. (you can change this parameter)
.Where(x => Total > 1)
// get it as array
.ToArray();
var result = string.Join(letters, "");
return result ;
}
And use it:
var text = GetLetterAllDuplicates("AABKCBD");
// should return "AB"
You can use:
String.IndexOf("A");
It will return the index of the First A Occurence.
If it returns -1, then there are not "A" occurrencies.
And here is my LINQ Implementation without using IndexOf:
string x = "AABCDEF";
List<char> repeatedCharacters = new List<char>();
var groupsOfChars = x.GroupBy(stringCharacter => stringCharacter);
groupsOfChars
.ToList()
.ForEach(item => {
if (item.Count() > 1) repeatedCharacters.Add(item.Key);
});
Or if you don't need the group:
string x = "AABCDEF";
List<char> repeatedCharacters = new List<char>();
x.GroupBy(stringCharacter => stringCharacter)
.ToList()
.ForEach(item => {
if (item.Count() > 1) repeatedCharacters.Add(item.Key);
});
And then you could check it:
repeatedCharacters.ForEach(item => {
Console.WriteLine(item.ToString());
});
//Since repeatedCharacters is an array, you can just simply do:
string stringOfRepeatedCharacters = repeatedCharacters.ToString();
//So you can easily convert the values to a String.
//[ 'A', 'B' ] is the result and it can be "AB".
it seems like bringing more wood into a forest, but it seems that some answers are incomplete or do not do exactly what was requested or are horrible complex:).
string input = "AABZFFZDCZZK";
//can handle null and empty string...
var rslt =
(string.IsNullOrEmpty(input) ? string.Empty : input)
.GroupBy(c => c)
.Select(gc => gc.Count() > 1 ? gc.Key : (char)0)
.Where(c => c != (char)0)
.OrderBy(c => c)//optional
.Aggregate(string.Empty, (c, n) => c + n)
;
The result is:
"AFZ"
The question was to provide back string with characters that occur multiple times.