Reading characters from a string and counting each one of them - c#

The issue i have with my code is as following: i cannot get my head around how to read each character and sum each up in one int for everyone at the end of all rotations. Here is my code:
class Program
{
static void Main()
{
SortedDictionary<string, int> text = new SortedDictionary<string, int>();
string[] characters = Console.ReadLine()
.Split()
.ToArray();
foreach (var character in characters)
{
if (text.ContainsKey(character))
{
text[character]++;
}
else
{
text.Add(character, 1);
}
}
foreach (var character in text)
{
Console.WriteLine($"{character.Key} -> {character.Value}");
}
}
}
I am reading here how many times a string exists in the Dictionary. What i need to get, written above, is different. Please help, thanks!

String.Split() is splitting on new lines by default so characters contains a single string with the whole line in it. If you want each of the characters, just get rid of the Split (and change the Dictionary KeyType to char to match the values):
SortedDictionary<char, int> text = new SortedDictionary<char, int>();
char[] characters = Console.ReadLine().ToArray();
// ...
https://www.ideone.com/hnMSv1
Since string implements IEnumerable<char> you actually don't even need to convert the characters into an array:
SortedDictionary<char, int> text = new SortedDictionary<char, int>();
string line = Console.ReadLine();
foreach( char character in line )
// ...
https://www.ideone.com/nLyBfC

You can use LINQ here because any string consists of char element. So, string type implements IEnumerable<char> interface:
string str = "aaabbc";
var res = str
.GroupBy(c => c)
.ToDictionary(g => new { g.Key, Count = g.Count() });
The example below demonstrates how you can get it without casting to dictionary but projecting an anonymous type and sort the number of characters in descending order:
var res2 = str
.GroupBy(c => c)
.Select(d => new { d.Key, Count = d.Count() })
.OrderByDescending(x => x.Count);

Related

Split string around colon

could you help splitting a string into key value pairs around the colon delimiter. I am having trouble with this.
eg.
"somekey:value1 value2 another:<value3 one_more:value4..value5"
output
<"somekey", "value1 value2">
<"another", "<value3">
<"one_more", "value4..value5">
This if you just want a simple conversion. you can also use regex.
private static Dictionary<string, string> Dictionary(string str)
{
var dictionary = new Dictionary<string, string>();
var splitOnSpace = str.Split(" ");
var value = string.Empty;
var key = "";
var i = 0;
while (i < splitOnSpace.Length)
{
var item = splitOnSpace[i];
if (item.Contains(":"))
{
var split = item.Split(':');
key = split[0];
value = split[1];
dictionary.Add(key, value);
}
else
{
value += " " + item;
dictionary[key] = value;
}
i++;
}
return dictionary;
}
The regex extracting such key-value pairs is
([^\s:]+):(.*?)(?=\s+[^\s:]+:|$)
(Demo)
The tricky part here is (?=\s+[^\s:]+:|$) lookahead, which tells the "match anything for value" regex ((.*?)) stop as soon as it encounters the next key preceded by some spaces (\s+[^\s:]+:) or simply end of string ($).
Then the match groups can be extracted as follows:
var input = "somekey:value1 value2 another:<value3 one_more:value4..value5";
var matches = Regex.Matches(input, #"([^\s:]+):(.*?)(?=\s+[^\s:]+:|$)");
var pairs = matches.Select(m => (m.Groups[1].Value, m.Groups[2].Value));
foreach (var (key, value) in pairs)
{
Console.WriteLine($"<\"{key}\": \"{value}\">");
}
Full demo
You can try this regex.
string givenString =
#"key1:value1 value2 key2:<value3 key3:value4..value5";
Dictionary<string, string> result1 = Regex
.Split(givenString, "([a-z0-9]+:)")
.Skip(1) // will skip the first empty
.Select((item, index) => new {
value = item.Trim(),
index = index / 2
})
.GroupBy(item => item.index)
.ToDictionary(chunk => chunk.First().value.TrimEnd(':'),
chunk => chunk.Last().value);

Retrieving Numeric value before a decimal in a string value

I am working on a routine in C#
I have a list of alphanumeric sheet numbers that I would like to retrieve the numbers before the decimal to use in my routine.
FP10.01-->10
M1.01-->1
PP8.01-->8
If possible, how can something like this be achieved as either a string or integer?
You could use a regex:
Regex r = new Regex("([0-9]+)[.]");
string s = "FP10.01";
var result = Convert.ToInt32(r.Match(s).Groups[1].ToString()); //10
string input = "FP10.01";
string[] _input = input.Split('.');
string num = find(_input[0]);
public string find(string input)
{
char[] _input = input.ToArray();
int number;
string result = null;
foreach (var item in _input)
{
if (int.TryParse(item.ToString(), out number) == true)
{
result = result + number;
}
}
return result;
}
To accumulate the resulting elements into a list, you can do something like:
List<string> myList = new List<string>(){ "FP10.01","M1.01", "PP8.01"};
List<int> resultSet =
myList.Select(e =>
Regex.Replace(e.Substring(0, e.IndexOf('.')), #"[^\d]", string.Empty))
.Select(int.Parse)
.ToList();
This will take each element in myList and in turn, take a substring of each element from index 0 until before the . and then replace all the non-numeric data with string.Empty and then finally parse the string element into an int and store it into a list.
another variant would be:
List<int> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.Select(int.Parse)
.ToList();
or if you want the elements to be strings then you could do:
List<string> resultSet =
myList.Select(e => e.Substring(0, e.IndexOf('.')))
.Select(e => string.Join(string.Empty, e.Where(char.IsDigit)))
.ToList();
To retrieve a single element of type string then you can create a helper function as such:
public static string GetValueBeforeDot(string input){
return input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a);
}
To retrieve a single element of type int then the helper function should be:
public static int GetValueBeforeDot(string input){
return int.Parse(input.Substring(0, input.IndexOf('.'))
.Where(char.IsDigit)
.Aggregate(string.Empty, (e, a) => e + a));
}
This approach removes alphabet characters by replacing them with an empty string. Splitting on the '.' character will leave you with a two element array consisting of numbers at index 0 and after decimal values at index 1.
string input = "FP10.01";
var result = Regex.Replace(input, #"([A-Za-z]+)", string.Empty).Split('.');
var beforeDecimalNumbers = result[0]; // 10
var afterDecimalNumbers = result[1]; // 01

Numbering - algorithm in a 2d array - "assign occurrence of a letter" - in c#

I need to create an algorithm in C# that basically counts the occurrence of a letter in the first column of an array and writes it into the 2nd column, for example:
a | 1
a | 2
b | 1
c | 1
a | 3
b | 2
b | 3
c | 2 (...)
I feel like using LINQ would do the trick, but for now I cannot think of the correct way.
Two separate 1d arrays would do fine, too, as long as the indices match.
Thanks for hints and thoughts!
You cannot mix types in a multi dimensional arrays. You could use Dictionary<char,int> instead where the key is the letter and the value is the occurence. However, that would not be the same as your char[] where letters can repeat.
So here is an approach than is more maintainable and readable using a custom struct with all relevant properties.
public struct LetterInfo
{
public char Letter { get; set; }
public int Occurence { get; set; }
public int Index { get; set; }
public override string ToString()
{
return string.Format("{0}:{1}->{2}", Index, Letter, Occurence);
}
}
Given that you have already a char[] and you want to have all missing informations you could get them with LINQ in this way:
char[] letters = {'a', 'a' , 'b', 'c', 'a', 'b', 'b', 'c'};
LetterInfo[] letterInfos = letters
.Select((c, i) => new { Letter = c, Index = i })
.GroupBy(x => x.Letter)
.SelectMany(xGrp => xGrp
.Select((x, i) => new LetterInfo
{
Letter = x.Letter, Occurence = i + 1,
Index = x.Index
}))
.OrderBy(li => li.Index)
.ToArray();
string output = string.Join(Environment.NewLine, letterInfos);
Console.Write(output);
Output:
0:a->1
1:a->2
2:b->1
3:c->1
4:a->3
5:b->2
6:b->3
7:c->2
Here's an approach without LINQ that should be more efficient but more memory-hungry (you can use List<LetterInfo> alone instead of the array to avoid another collection):
var latterList = new List<LetterInfo>();
var dict = new Dictionary<char, int>();
for (int i = 0; i < letters.Length; i++)
{
char c = letters[i];
int occ = 0;
dict.TryGetValue(c, out occ);
dict[c] = ++occ;
var li = new LetterInfo { Letter = c, Index = i, Occurence = occ };
latterList.Add(li);
}
letterInfos = latterList.ToArray();
For the sake of completeness, here's the dictionary approach mentioned above to lookup a char and it's occurence:
Dictionary<char, int> letterCount = letters
.GroupBy(l => l)
.ToDictionary(g => g.Key, g => g.Count());
If you want to know how often the letter a is in the array:
int aCount = letterCount['a']; // throws an exception if a is not in the dictionary
or safer:
int aCount = 0;
bool contains = letterCount.TryGetValue('a', out aCount);
So you would need a dictionary to keep track of your letters.
Dictionary<char, int> CharacterTracker
if we have
char[] characters
int[] occurrences
Then I would just iterate through characters. If we don't have that character in our Dictionary, then we put '1' in the corresponding occurences array, and character as a key in the Dictionary and 1 as its value. If we do have that character in our Dictionary, increment the value for the Dictionary[character], and put that value in occurences
This might be one of the cases where LINQ offers little benefit.
var letters = "aabcabbc";
var dict = new Dictionary<char, int>();
var counts = letters.Select(letter =>
{
int count;
dict.TryGetValue(letter, out count); // defaults to 0 if not found
dict[letter] = ++count;
return new { Letter = letter, Count = count };
});
Obligatory attempt in Linq (note: not a recommended solution!). It's getting the result as an IEnumerable<Tuple<char, int>>...
char[] chars = "aabcabbc".ToCharArray();
var result = Enumerable.Range(0, chars.Length).Select(i => new Tuple<char, int>(chars[i], chars.Take(i + 1).Count(c => c == chars[i])));
Why using hammers to smash flies ? Use an array of counters indexed by letters. A dictionary brings no benefit here. And LINQ is a big sledgehammer.
Pseudocode:
: Initialize
for c in "a..z"
counter[c]= 0
: Count
for c in string
counter[c]++

Sorting a string based on prefixes

If you are given an array with random prefixes, like this:
DOG_BOB
CAT_ROB
DOG_DANNY
MOUSE_MICKEY
DOG_STEVE
HORSE_NEIGH
CAT_RUDE
HORSE_BOO
MOUSE_STUPID
How would i go about sorting this so that i have 4 different arrays/lists of strings?
So the end result would give me 4 string ARRAYS or lists with
DOG_BOB,DOG_DANNY,DOG_STEVE <-- Array 1
HORSE_NEIGH, HORSE_BOO <-- Array 2
MOUSE_MICKEY, MOUSE_STUPID <-- Array 3
CAT_RUDE, CAT_ROB <-- Array 4
sorry about the names i just made them up lol
var fieldNames = typeof(animals).GetFields()
.Select(field => field.Name)
.ToList();
List<string> cats = new List<string>();
List<string> dogs = new List<string>();
List<string> mice= new List<string>();
List<string> horse = new List<string>();
foreach (var n in fieldNames)
{
var fieldValues = typeof(animals).GetField(n).GetValue(n);"
//Here's what i'm trying to do, with if statements
if (n.ToString().ToLower().Contains("horse"))
{
}
}
So i need them to be splitted into STRING ARRAYS/STRING LISTS and NOT just strings
string[] strings = new string[] {
"DOG_BOB",
"CAT_ROB",
"DOG_DANNY",
"MOUSE_MICKEY",
"DOG_STEVE",
"HORSE_NEIGH",
"CAT_RUDE",
"HORSE_BOO",
"MOUSE_STUPID"};
string[] results = strings.GroupBy(s => s.Split('_')[0])
.Select(g => String.Join(",",g))
.ToArray();
Or maybe something like this
List<List<string>> res = strings.ToLookup(s => s.Split('_')[0], s => s)
.Select(g => g.ToList())
.ToList();
var groups = fieldNames.GroupBy(n => n.Split('_')[0]);
Usage
foreach(var group in groups)
{
// group.Key (DOG, HORSE, CAT, etc)
foreach(var name in group)
// all names groped by prefix
}
foreach (String s in strings)
{
if (s.StartsWith("CAT_")
cats.Add(s);
else if (s.StartsWith("HORSE_")
horses.Add(s);
// ...
}
Or:
foreach (String s in strings)
{
String[] split = s.Split(new Char [] { '_' });
if (split[0].Equals("CAT")
cats.Add(s);
else if (split[0].Equals("HORSE")
horses.Add(s);
// ...
}
But I would prefer the first one.
Algorithmically, I'd do the following:
Parse out all unique prefixes by using the "_" as your delimeter.
Loop through your list of prefixes.
2a. Retrieve any values that have your prefix (loop/find/regex/depends on structure)
2b. Place retrieved values in a List.
2c. Sort list.
Output your results, or do what you need with your collections.
You can order the list up front and sort by prefix:
string[] input = new string[] {"DOG_BOB","CAT_ROB","DOG_DANNY","MOUSE_MICKEY","DOG_STEVE","HORSE_NEIGH","CAT_RUDE","HORSE_BOO","MOUSE_STUPID"};
string[] sortedInput = input.OrderBy(x => x).ToArray();
var distinctSortedPrefixes = sortedInput.Select(item => item.Split('_')[0]).Distinct().ToArray();
Dictionary<string, string[]> orderedByPrefix = new Dictionary<string, string[]>();
for (int prefixIndex = 0; prefixIndex < distinctSortedPrefixes.Length; prefixIndex++)
{
string prefix = distinctSortedPrefixes[prefixIndex];
var group = input.Where(item => item.StartsWith(prefix)).ToArray();
orderedByPrefix.Add(prefix, group);
}
With LINQ, using something like
names.GroupBy(s => s.Substring(0, s.IndexOf("_"))) // group by prefix
.Select(g => string.Join(",", g)) // join each group with commas
.ToList(); // take the results
See it in action (some extra .ToArray() calls included for .NET 3.0 compatibility)
This LINQ expression does what you want.
var result = data.GroupBy(data.Split('_')[0])
.Select(group => String.Join(", ", group))
.ToList();
For a list of lists of strings use this expression.
var result = data.GroupBy(data.Split('_')[0])
.Select(group => group.ToList())
.ToList();

Dictionary<string, int> increase value

I have a Dictionary<string, int> and I am reading some strings from a list... I want to add them in the dictionary, but if the string is already in the dictionary, I want its value to increase by 1.
The code I tried is as below, but there are some strings that are increased with every input.. Is something wrong?
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (String recordline in tags)
{
String recordstag = recordline.Split('\t')[1];
String tagToDic = recordstag.Substring(0, (recordstag.Length-1) );
if (dictionary.ContainsKey(tagToDic) == false)
{
dictionary.Add(tagToDic, 1);
}
else
{
try
{
dictionary[tagToDic] = dictionary[tagToDic] + 1;
}
catch (KeyNotFoundException ex)
{
System.Console.WriteLine("X" + tagToDic + "X");
dictionary.Add(tagToDic, 1);
}
}
}
EDIT: To answer your comments... I am removing the last char of the string because it is always a blank space...
My input is like:
10000301 business 0 0,000
10000301 management & auxiliary services 0 0,000
10000316 demographie 0 0,000
10000316 histoire de france 0 0,000
10000347 economics 0 0,000
10000347 philosophy 1 0,500
and i want only the string like "business" or "management & auxiliary services" etc.
You are splitting each string in the input string array and selecting the 2nd string in the string array. Then you are removing the last character of this 2nd string using SubString. Hence all strings that differ only in the last character would be considered the same and incremented. Thats why you might be seeing "some strings that are increased with every input".
EDIT: If the purpose of removing the last char is to remove space, Use String.Trim instead.
Another edit is using TryGetValue instead of ContainsKey which performs better to increment your value. Code has been edited below.
Try this:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach(string recordline in tags)
{
string recordstag = recordline.Split('\t')[1].Trim();
int value;
if (!dictionary.TryGetValue(recordstag, out value))
dictionary.Add(recordstag, 1);
else
dictionary[recordstag] = value + 1;
}
No need for a dictionary, can be solved using this Linq query.
(Assuming you want the complete string after \t)
var q =
from s in tags.Select (t => t.Substring(t.IndexOf("\t")))
group s by s into g
select new
{
g.Key,
Count = g.Count()
};
And if you need it as a dictionary just add:
var dic = q.ToDictionary (x => x.Key, x => x.Count);
Your input string first split and then substring of it returned to tagToDic, So maybe n strings have a same tagToDic.
Extension method
public static void Increment(this Dictionary<string, int> dictionary, string key)
{
int val;
dictionary.TryGetValue(key, out val);
if (val != null)
dictionary[key] = val + 1;
}
Dictionary<string, int> dictionary = new Dictionary<string, int>();
// fill with some data
dictionary.Increment("someKey");
It's probably easier just to re-add the dictionary value after you retrieve the count from the existing one.
Here's some psuedo code to handle the look up logic.
Dictionary<string, int> _dictionary = new Dictionary<string, int>();
private void AdjustWordCount(string word)
{
int count;
bool success = _dictionary.TryGetValue(word, out count);
if (success)
{
//Remove it
_dictionary.Remove(word);
//Add it back in plus 1
_dictionary.Add(word, count + 1);
}
else //could not get, add it with a count of 1
{
_dictionary.Add(word, 1);
}
}
How about:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
string delimitedTags = "some tab delimited string";
List<string> tags = delimitedTags.Split(new char[] {'\t'}, StringSplitOptions.None).ToList();
foreach (string tag in tags.Distinct())
{
dictionary.Add(tag, tags.Where(t => t == tag).Count());
}
If you have them in a list you could just group them and make your list.
list.GroupBy(recordline => recordline.Split('\t').Substring(0, (recordstag.Length-1),
(key, ienum) => new {word = key, count = ienum.Count()});
Then you can put that in a dictionary or iterate it or something.
Your dictionary code looks like it will function the way you expect.
My best guess is that your string-splitting code is not working correctly.
You'd have to give us some sample inputs to verify this though.
Anyway, your entire block of code could be simplified and rewritten with LINQ as:
var dictionary = tags
.Select(t => {
var recordstag = t.Split('\t')[1];
return recordstag.Substring(0, recordstag.Length-1);
})
.GroupBy(t => t)
.ToDictionary(k => k.Key, v => v.Count())
;

Categories

Resources