views:

10264

answers:

8

I am doing something where I realised I wanted to count how many /s I could find in a string, and then it struck me, that there were about several ways to do it, but couldn't decide on what the best (or easiest) was.

At the moment I'm going with something like:

string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;

But I don't like it at all, any takers?

I don't really want to dig out RegEx for this, do I?

EDIT: I know my string is going to have the term I'm searching for, so you can assume that...

+7  A: 
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

Has to be faster than the source.Replace() by itself.

bobwienholt
FASTER is better
DrG
You could gain a marginal improvement by switching to a for instead of a foreach, but only a tiny, tiny bit.
Mark
+15  A: 

LINQ works on all collections, and since strings are just a collection of characters, how about this nice little one-liner:

var count = source.Count(c => c == '/');
Judah Himango
I am LIKING one liners!
DrG
Wow... that's pretty sweet.
bobwienholt
They're nice! I love LINQ, I must say; it replaces swaths of my own crappy algorithms.
Judah Himango
Is it really worth using var there? Is there any chance Count will be replaced with something that doesn't return an int?
Whatsit
@Whatsit: you can type 'var' with just your left hand while 'int' requires both hands ;)
Sean Bright
Whatsit, you may prefer int there. I'll often use var for locals even if the variable type is obvious. Personal preference.
Judah Himango
`int` letters all reside in home keys, while `var` doesn't. uh.. wait, i'm using Dvorak
Michael Buen
+19  A: 

If you're using .NET 3.5 you can do this in a one-liner with LINQ:

int count = source.Count(f => f == '/');

If you don't want to use LINQ you can do it with:

int count = source.Split('/').Length - 1;


You might be surprised to learn that your original technique seems to be about 30% faster than either of these! I've just done a quick benchmark with "/once/upon/a/time/" and the results are as follows:

Your original = 12s
source.Count = 19s
source.Split = 17s
foreach (from bobwienholt's answer) = 10s

(The times are for 50,000,000 iterations so you're unlikely to notice much difference in the real world.)

LukeH
Did you know you can call Count(predicate) on a string without having to convert it to a character array? See my answer above.
Judah Himango
@Judah, You're right, but weirdly VS2008 isn't giving me intellisense for source.Count - it compiles and runs fine though, so +1 for your answer.
LukeH
very surprised to hear my code was quicker than anything!
DrG
@in.spite, Your original code also has the advantage that with a small tweak (as in ZombieSheep's answer) you can search for strings of arbitrary length rather than just single character.
LukeH
Yeah, VS hides LINQ extension methods on the string class. I guess they figured devs wouldn't want all those extension methods to show up on the string class. Probably a wise decision.
Judah Himango
+1  A: 

These both only work for single-character search terms...

countOccurences("the", "the answer is the answer");

int countOccurences(string needle, string haystack)
{
    return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}

may turn out to be better for longer needles...

But there has to be a more elegant way. :)

ZombieSheep
To account for multi-character replacements. Without it, counting "the" in "the test is the key" would return 6.
ZombieSheep
i got it sorry! deleted my comment :D
DrG
+9  A: 

If you want to be able to search for whole strings, and not just characters:

src.Select((c, i) => src.Substring(i)).Count(sub => sub.StartsWith(target))

Read as "for each character in the string, take the rest of the string starting from that character as a substring; count it if it starts with the target string."

mquander
+2  A: 

source.Split('/').Count - 1

Brian Rudolph
This is what I do. And `source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1` for multi-character separators.
bzlm
A: 

I like your original solution better than any of the other ways.

ctrlShiftBryan
A: 

source.Length - (source.Length - source.Trim('/').Length);

Not working :0 Sorry

Eric
source.Length - source.Replace("/", "").Length; ... Replace, not Trim
DrG