Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I want to get count the elements in an array but without linq
Example:
string a = "cat";
string b = "dog";
string c = "cat";
string d = "horse";
var list = new List<string>();
list.Add(a);
list.Add(b);
list.Add(c);
list.Add(d);
And
desired result is : cat=2, dog=1, horse=1
Here's one way I could think of using a Dictionary<string, int>:
public static Dictionary<string, int> GetObjectCount(List<string> items)
{
// Dictionary object to return
Dictionary<string, int> keysAndCount = new Dictionary<string, int>();
// Iterate your string values
foreach(string s in items)
{
// Check if dictionary contains the key, if so, add to count
if (keysAndCount.ContainsKey(s))
{
keysAndCount[s]++;
}
else
{
// Add key to dictionary with initial count of 1
keysAndCount.Add(s, 1);
}
}
return keysAndCount;
}
Then get the result back and print to console:
Dictionary<string, int> dic = GetObjectCount(list);
//Print to Console
foreach(string s in dic.Keys)
{
Console.WriteLine(s + " has a count of: " + dic[s]);
}
I am not sure why are you looking for LINQ less solution for this as this could be done very easily and efficiently by it. I strongly suggest you to use it and do it like below :
var _group = list.GroupBy(i => i);
string result = "";
foreach (var grp in _group)
result += grp.Key + ": " + grp.Count() + Environment.NewLine;
MessageBox.Show(result);
Otherwise you can do it like below if you really unable to use LINQ :
Dictionary<string, int> listCount = new Dictionary<string, int>();
foreach (string item in list)
if (!listCount.ContainsKey(item))
listCount.Add(item, 1);
else
listCount[item]++;
string result2 = "";
foreach (KeyValuePair<string, int> item in listCount)
result2 += item.Key + ": " + item.Value + Environment.NewLine;
MessageBox.Show(result2);
The simple solution to your issue is a foreach loop.
string[] myStrings = new string[] { "Cat", "Dog", "Horse", "CaT", "cat", "DOG" };
Console.WriteLine($"There are {GetCount(myStrings, "cat");} cats.");
static int GetCount(string[] strings, string searchTerm) {
int result = 0;
foreach (string s in strings)
if (s == searchTerm)
result++;
return result;
}
Linq does this under the hood. However, unless this is either for optimization of large lists or for learning experience, Linq should be your preferred choice if you know how to use it. It exists to make your life easier.
Another implementation of this would be to simplify the number of calls you need and just write the output in the method:
string[] myStrings = new string[] { "Cat", "Dog", "Horse", "CaT", "cat", "DOG" };
CountTerms(myStrings, "cat", "dog");
Console.ReadKey();
static void CountTerms(string[] strings, params string[] terms) {
foreach (string term in terms) {
int result = 0;
foreach (string s in strings)
if (s == term)
result++;
Console.WriteLine($"There are {result} instances of {term}");
}
}
With that said, I heavily recommend Ryan Wilson's answer. His version simplifies the task at hand. The only downside to his implementation is if you are implementing this in a singular manner the way List<string>.Count(c => c == "cat") would.
You could try something like:
public int countOccurances(List<string> inputList, string countFor)
{
// Identifiers used are:
int countSoFar = 0;
// Go through your list to count
foreach (string listItem in inputList)
{
// Check your condition
if (listItem == countFor)
{
countSoFar++;
}
}
// Return the results
return countSoFar;
}
this will give you the count for any sting you give it. As always there is a better way but this is a good start.
Or if you want:
public string countOccurances(List<string> inputList, string countFor)
{
// Identifiers used are:
int countSoFar = 0;
string result = countFor;
// Go through your list to count
foreach (string listItem in inputList)
{
// Check your condition
if (listItem == countFor)
{
countSoFar++;
}
}
// Return the results
return countFor + " = " countSoFar;
}
Or an even better option:
private static void CountOccurances(List<string> inputList, string countFor)
{
int result = 0;
foreach (string s in inputList)
{
if (s == countFor)
{
result++;
}
}
Console.WriteLine($"There are {result} occurrances of {countFor}.");
}
Linq is supposed to make developer's life easy. Anyway you could make something like this:
string a = "cat";
string b = "dog";
string c = "cat";
string d = "horse";
var list = new List<string>();
list.Add(a);
list.Add(b);
list.Add(c);
list.Add(d);
var result = GetCount(list);
Console.WriteLine(result);
Console.ReadLine();
static string GetCount(List<string> obj)
{
string result = string.Empty;
int cat = 0;
int dog = 0;
int horse = 0;
foreach (var item in obj)
{
switch (item)
{
case "dog":
dog++;
break;
case "cat":
cat++;
break;
case "horse":
horse++;
break;
}
}
result = "cat = " + cat.ToString() + " dog = " + dog.ToString() + " horse = " + horse.ToString();
return result;
}
I have a string that looks like this. It's not JSON and not XML.
{Foo={name=My Foo Value, active=true, some date=20170630}, Bar={name=My Bar Value}, Key With Space={name=Foo Bar, active=false}}
Here are the assumptions:
Objects are enclosed by {}
Keys can have a space in the name
Keys are separated by a space and a comma (,)
Values are assigned to keys by an equal sign
Keys can have multiple values and values are enclosed by {}, values inside the value are separated by a space and a comma (,). For example, this is a single key with three values: {My Foo Key={one=true, two=true, third value=false}}
My strategy is to deserialize to Dictionary<string, object at first, worry about recursion later. Any suggestions (existing library?) appreciated!
Here is what I have
var stringContentTrimmed = stringContent.Substring(1, stringContent.Length - 2);
var objects = stringContentTrimmed.Split(',')
.Select(x => x.Trim())
.Where(x => !String.IsNullOrWhiteSpace(x));
TLDR. The Split function is also splitting up my values, which isn't what I want.
Really you need to have a proper specification or grammar for this but I'm going to take a wild guess and say that there isn't one, and if there was, actual values would not conform to it.
Your best bet might be:
Eliminate any whitespace adjacent to = { } or , characters
Replace any , with ","
Replace any = with "="
replace any { with {"
replace any } with "}
replace any "{ with {
replace any }" with }
replace any = with :
Then treat as JSON.
I tried this with your example and it worked. Whether it will work with your actual values I have no idea - this will depend on whether they stick to the restrictions you have described. If keys or values embed any of "{}:=, or if leading or trailing spaces are significant, then it will not work.
I created a method GetObjects below which returns a Dictionary<string, string> of the top-level objects and the raw content inside. Another method, Merge returns a nested dictionary by calling GetValues to extract the key-value pairs from the object content.
Using your example string, the Merge method returns this:
class Program
{
static void Main(string[] args)
{
var str = "{Foo={name=Foo Value, active=true, some date=20170630}, Bar={name#=My Bar Value}, Key With Space={name=Foo Bar, active=false}}";
var values = GetObjects(str);
Dictionary<string, Dictionary<string, string>> objects = Merge(values);
}
public static Dictionary<string, Dictionary<string, string>> Merge(Dictionary<string, string> input)
{
var output = new Dictionary<string, Dictionary<string, string>>();
foreach (var key in input.Keys)
{
var value = input[key];
var subValues = GetValues(value);
output.Add(key, subValues);
}
return output;
}
public static Dictionary<string, string> GetObjects(string input)
{
var objects = new Dictionary<string, string>();
var objectNames = new Queue<string>();
var objectBuffer = string.Empty;
foreach (var c in input)
{
if (char.Equals('{', c))
{
if (!string.IsNullOrEmpty(objectBuffer))
{
var b = objectBuffer.Trim('{', '}', ',', ' ', '=');
objectNames.Enqueue(b);
}
objectBuffer = string.Empty;
}
if (char.Equals('}', c))
{
if (objectNames.Count > 0)
{
var b = objectBuffer.Trim('{');
var key = objectNames.Dequeue();
objects.Add(key, b);
}
objectBuffer = string.Empty;
}
objectBuffer += c;
}
return objects;
}
private static Dictionary<string, string> GetValues(string input)
{
var output = new Dictionary<string, string>();
var values = input.Split(new string[] { ", " }, System.StringSplitOptions.None);
foreach (var val in values)
{
var parts = val.Split('=');
if (parts.Length == 2)
{
var key = parts[0].Trim(' ');
var value = parts[1].Trim(' ');
output.Add(key, value);
}
}
return output;
}
}
I have made a function which can replace the position of the chars if they are standing in my list
Code:
public string NoSimilarChar(string password)
{
var listOfSimilarCharacters = new Dictionary<string, string>();
listOfSimilarCharacters.Add("l", "i");
listOfSimilarCharacters.Add("1", "i");
listOfSimilarCharacters.Add("O", "0");
// Iterate through each character
for (int i = 0; i < password.Length; i++)
{
var currentCharacter = password[i].ToString();
// check if the current char exists in either the key or the value of the list of similar characters
if (listOfSimilarCharacters.Keys.Contains(currentCharacter) || listOfSimilarCharacters.Values.Contains(currentCharacter))
{
currentCharacter = currentCharacter.Remove(currentCharacter.Length - 1, 1) + ",";
}
}
return password;
}
Now i want to know how to load the function NoSimilarChar over when the characters is remove
i thought something like this:
if (listOfSimilarCharacters.Keys.Contains(currentCharacter) || listOfSimilarCharacters.Values.Contains(currentCharacter))
{
currentCharacter = currentCharacter.Remove(currentCharacter.Length - 1, 1) + ",";
NoSimilarChar(password);
}
but i think this is not good because he then stays in a loop.
///for replacing
foreach (KeyValuePair<string, string> item in listOfSimilarCharacters)
{
password = password.Replace(item.Key, item.Value);
}
///for removing
foreach (KeyValuePair<string, string> item in listOfSimilarCharacters)
{
if (password.IndexOf(item.Key) >= 0)
password = password.Remove(password.IndexOf(item.Key), 1);
}
try this simpler one
var charsThatCannotbeinUserPwd = new[] {'1', 'l', 'O', '0', 'i'};
// Iterate through each character
var builder = new StringBuilder();
for (int i = 0; i < password.Length; i++)
{
var currentCharacter = password[i];
if (!charsThatCannotbeinUserPwd.Any(x => x.Equals(currentCharacter)))
builder.Append(currentCharacter);
}
return builder.ToString();
It looks like you want to remove a set of characters from you password. If that is the case then you don't need to use a Dictionary. A Dictionary would make more sense if you wanted to replace one character with another. Additionally you do not need to use recursion here. I believe all you need is an array of the characters you want to remove and a simple loop to remove them.
public string NoSimilarChar(string password)
{
string[] charsToRemove = new string[] { "l", "i", "1", "0", "O" }
foreach (string charToRemove in charsToRemove)
{
password = password.Replace(charToRemove, "");
}
return password;
}
FYI: I've defined the array of characters as strings because you will want to replace the character with an empty string and there is no empty character.
The loop:
var pattern = _dict[key];
string before;
do
{
before = pattern;
foreach (var pair in _dict)
if (key != pair.Key)
pattern = pattern.Replace(string.Concat("{", pair.Key, "}"), string.Concat("(", pair.Value, ")"));
} while (pattern != before);
return pattern;
It just does a repeated find-and-replace on a bunch of keys. The dictionary is just <string,string>.
I can see 2 improvements to this.
Every time we do pattern.Replace it searches from the beginning of the string again. It would be better if when it hit the first {, it would just look through the list of keys for a match (perhaps using a binary search), and then replace the appropriate one.
The pattern != before bit is how I check if anything was replaced during that iteration. If the pattern.Replace function returned how many or if any replaces actually occured, I wouldn't need this.
However... I don't really want to write a big nasty thing class to do all that. This must be a fairly common scenario? Are there any existng solutions?
Full Class
Thanks to Elian Ebbing and ChrisWue.
class FlexDict : IEnumerable<KeyValuePair<string,string>>
{
private Dictionary<string, string> _dict = new Dictionary<string, string>();
private static readonly Regex _re = new Regex(#"{([_a-z][_a-z0-9-]*)}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public void Add(string key, string pattern)
{
_dict[key] = pattern;
}
public string Expand(string pattern)
{
pattern = _re.Replace(pattern, match =>
{
string key = match.Groups[1].Value;
if (_dict.ContainsKey(key))
return "(" + Expand(_dict[key]) + ")";
return match.Value;
});
return pattern;
}
public string this[string key]
{
get { return Expand(_dict[key]); }
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
foreach (var p in _dict)
yield return new KeyValuePair<string,string>(p.Key, this[p.Key]);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Example Usage
class Program
{
static void Main(string[] args)
{
var flex = new FlexDict
{
{"h", #"[0-9a-f]"},
{"nonascii", #"[\200-\377]"},
{"unicode", #"\\{h}{1,6}(\r\n|[ \t\r\n\f])?"},
{"escape", #"{unicode}|\\[^\r\n\f0-9a-f]"},
{"nmstart", #"[_a-z]|{nonascii}|{escape}"},
{"nmchar", #"[_a-z0-9-]|{nonascii}|{escape}"},
{"string1", #"""([^\n\r\f\\""]|\\{nl}|{escape})*"""},
{"string2", #"'([^\n\r\f\\']|\\{nl}|{escape})*'"},
{"badstring1", #"""([^\n\r\f\\""]|\\{nl}|{escape})*\\?"},
{"badstring2", #"'([^\n\r\f\\']|\\{nl}|{escape})*\\?"},
{"badcomment1", #"/\*[^*]*\*+([^/*][^*]*\*+)*"},
{"badcomment2", #"/\*[^*]*(\*+[^/*][^*]*)*"},
{"baduri1", #"url\({w}([!#$%&*-\[\]-~]|{nonascii}|{escape})*{w}"},
{"baduri2", #"url\({w}{string}{w}"},
{"baduri3", #"url\({w}{badstring}"},
{"comment", #"/\*[^*]*\*+([^/*][^*]*\*+)*/"},
{"ident", #"-?{nmstart}{nmchar}*"},
{"name", #"{nmchar}+"},
{"num", #"[0-9]+|[0-9]*\.[0-9]+"},
{"string", #"{string1}|{string2}"},
{"badstring", #"{badstring1}|{badstring2}"},
{"badcomment", #"{badcomment1}|{badcomment2}"},
{"baduri", #"{baduri1}|{baduri2}|{baduri3}"},
{"url", #"([!#$%&*-~]|{nonascii}|{escape})*"},
{"s", #"[ \t\r\n\f]+"},
{"w", #"{s}?"},
{"nl", #"\n|\r\n|\r|\f"},
{"A", #"a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?"},
{"C", #"c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?"},
{"D", #"d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?"},
{"E", #"e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?"},
{"G", #"g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g"},
{"H", #"h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h"},
{"I", #"i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i"},
{"K", #"k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k"},
{"L", #"l|\\0{0,4}(4c|6c)(\r\n|[ \t\r\n\f])?|\\l"},
{"M", #"m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m"},
{"N", #"n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n"},
{"O", #"o|\\0{0,4}(4f|6f)(\r\n|[ \t\r\n\f])?|\\o"},
{"P", #"p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p"},
{"R", #"r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r"},
{"S", #"s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s"},
{"T", #"t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t"},
{"U", #"u|\\0{0,4}(55|75)(\r\n|[ \t\r\n\f])?|\\u"},
{"X", #"x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x"},
{"Z", #"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"},
{"Z", #"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"},
{"CDO", #"<!--"},
{"CDC", #"-->"},
{"INCLUDES", #"~="},
{"DASHMATCH", #"\|="},
{"STRING", #"{string}"},
{"BAD_STRING", #"{badstring}"},
{"IDENT", #"{ident}"},
{"HASH", #"#{name}"},
{"IMPORT_SYM", #"#{I}{M}{P}{O}{R}{T}"},
{"PAGE_SYM", #"#{P}{A}{G}{E}"},
{"MEDIA_SYM", #"#{M}{E}{D}{I}{A}"},
{"CHARSET_SYM", #"#charset\b"},
{"IMPORTANT_SYM", #"!({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T}"},
{"EMS", #"{num}{E}{M}"},
{"EXS", #"{num}{E}{X}"},
{"LENGTH", #"{num}({P}{X}|{C}{M}|{M}{M}|{I}{N}|{P}{T}|{P}{C})"},
{"ANGLE", #"{num}({D}{E}{G}|{R}{A}{D}|{G}{R}{A}{D})"},
{"TIME", #"{num}({M}{S}|{S})"},
{"PERCENTAGE", #"{num}%"},
{"NUMBER", #"{num}"},
{"URI", #"{U}{R}{L}\({w}{string}{w}\)|{U}{R}{L}\({w}{url}{w}\)"},
{"BAD_URI", #"{baduri}"},
{"FUNCTION", #"{ident}\("},
};
var testStrings = new[] { #"""str""", #"'str'", "5", "5.", "5.0", "a", "alpha", "url(hello)",
"url(\"hello\")", "url(\"blah)", #"\g", #"/*comment*/", #"/**/", #"<!--", #"-->", #"~=",
"|=", #"#hash", "#import", "#page", "#media", "#charset", "!/*iehack*/important"};
foreach (var pair in flex)
{
Console.WriteLine("{0}\n\t{1}\n", pair.Key, pair.Value);
}
var sw = Stopwatch.StartNew();
foreach (var str in testStrings)
{
Console.WriteLine("{0} matches: ", str);
foreach (var pair in flex)
{
if (Regex.IsMatch(str, "^(" + pair.Value + ")$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture))
Console.WriteLine(" {0}", pair.Key);
}
}
Console.WriteLine("\nRan in {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
Purpose
For building complex regular expressions that may extend eachother. Namely, I'm trying to implement the css spec.
I think it would be faster if you look for any occurrences of {foo} using a regular expression, and then use a MatchEvaluator that replaces the {foo} if foo happens to be a key in the dictionary.
I have currently no visual studio here, but I guess this is functionally equivalent with your code example:
var pattern = _dict[key];
bool isChanged = false;
do
{
isChanged = false;
pattern = Regex.Replace(pattern, "{([^}]+)}", match => {
string matchKey = match.Groups[1].Value;
if (matchKey != key && _dict.ContainsKey(matchKey))
{
isChanged = true;
return "(" + _dict[matchKey] + ")";
}
return match.Value;
});
} while (isChanged);
Can I ask you why you need the do/while loop? Can the value of a key in the dictionary again contain {placeholders} that have to be replaced? Can you be sure you don't get stuck in an infinite loop where key "A" contains "Blahblah {B}" and key "B" contains "Blahblah {A}"?
Edit: further improvements would be:
Using a precompiled Regex.
Using recursion instead of a loop (see ChrisWue's comment).
Using _dict.TryGetValue(), as in Guffa's code.
You will end up with an O(n) algorithm where n is the size of the output, so you can't do much better than this.
You should be able to use a regular expression to find the matches. Then you can also make use of the fast lookup of the dictionary and not just use it as a list.
var pattern = _dict[key];
bool replaced = false;
do {
pattern = Regex.Replace(pattern, #"\{([^\}]+)\}", m => {
string k = m.Groups[1].Value;
string value;
if (k != key && _dict.TryGetValue(k, out value) {
replaced = true;
return "(" + value + ")";
} else {
return "{" + k + "}";
}
});
} while (replaced);
return pattern;
You can implement the following algorithm:
Search for { in source string
Copy everything upto { to StringBuilder
Find matching } (the search is done from last fond position)
Compare value between { and } to keys in your dictionary
If it matches copy to String builder ( + Value + )
Else copy from source string
If source string end is not reached go to step 1
Could you use PLINQ at all?
Something along the lines of:
var keys = dict.KeyCollection.Where(k => k != key);
bool replacementMade = keys.Any();
foreach(var k in keys.AsParallel(), () => {replacement code})