-1

Im working to correctly map links on websites.

I need to be able to count how often ../ occurs in a string. At this moment I have a function that loops through the string and counts, while this works, im looking for a Linq solution.

I know that I can count with a single character like this

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

But, can I, by using LINQ , count how often the pattern ../ occurs? Is this possible?

Mad Dog Tannen
  • 7,129
  • 5
  • 31
  • 55
  • 3
    Why do you want to use LINQ, this seems like a job for a regex? – Ben Robinson Oct 27 '14 at 09:46
  • 3
    I have a hammer. I need to drive this screw into the wall. How do I hit it with the hammer? – Kris Vandermotten Oct 27 '14 at 09:46
  • Fundementally if you use LINQ 'directly' on a string you're using LINQ on an IEnumerable, It will be easier NOT to use LINQ as ALL you can really get out of LINQ is an index enumeration. I'd look at http://stackoverflow.com/questions/541954/how-would-you-count-occurrences-of-a-string-within-a-string – tolanj Oct 27 '14 at 09:49
  • Please don't call a __'string'__ like "../" a __'pattern'__! A pattern would be "*.bmp" or the like.. – TaW Oct 27 '14 at 09:50
  • RegEx will be the fastest with String.Replace coming in 2nd. LINQ is slow and tends to crash on large data.. See [here](http://stackoverflow.com/questions/541954/how-would-you-count-occurrences-of-a-string-within-a-string) - Please note: On that post almost __all answers, including the accepted one are wrong!__ Ramos has got it right, though.. And the OP in his own question..!! – TaW Oct 27 '14 at 09:54

5 Answers5

2

You can do that nicely with Regex

var dotdotslash=new Regex(@"\.\./");
string test="../../bla/../";
int count=dotdotslash.Matches(test).Count;

3
Dmitry Ledentsov
  • 3,620
  • 18
  • 28
1

Yes, it's possible, but it's very awkward, it will be slow, and it will be hard to read. Don't use it.

How would you count occurrences of a string within a string?

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

Alternatively, this looks pretty beautiful:

public static class StringExtensions
{
    public static IEnumerable<int> IndexOfAll(this string input, string value){
        var currentIndex = 0;

        while((currentIndex = input.IndexOf(value, currentIndex)) != -1)
            yield return currentIndex++;
    }
}

and usage:

"TESTHATEST"
    .IndexOfAll("TEST")
    .Count()
    .Dump();
Community
  • 1
  • 1
Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78
  • 1
    @SimonWhitehead; what do you mea by that? The above LINQ seems faulty because .Contains("string") doesn't make sense. – Erti-Chris Eelmaa Oct 27 '14 at 09:49
  • The above link is one of the strangest posts I have seen on SO ever..! Ridiculous scores and almost all answers wrong (in that they only can count characters, not substrings.. And besides RegEx the OP has a funny solution, that beats the rest by doing a String.Replace..! – TaW Oct 27 '14 at 10:02
1

You could use this extension method:

public static int ContainsCount(this string input, string subString, bool countIntersecting = true, StringComparison comparison = StringComparison.CurrentCulture)
{
    int occurences = 0;
    int step = countIntersecting ? 1 : subString.Length;
    int index = -step;
    while ((index = input.IndexOf(subString, index + step, comparison)) >= 0)
        occurences++;
    return occurences;
}

which returns the number of sub-strings in a given string with pure string-methods:

int count = Href.ContainsCount("../");

String-methods are superior to other methods which use LINQ or regex in terms of efficiency.

This method supports counting intersecting sub-strings(default) and non-overlapping sub-strings.

This shows the difference:

string str = "ottotto";
int count = str.ContainsCount("otto");      // 2
count = str.ContainsCount("otto", false);   // 1
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
0

Regular expression (see Dmitry Ledentsov's answer) is much better here; however Linq is also possible:

  String source = @"abc../def../";

  // 2
  int result = source
    .Where((item, index) => source.Substring(index).StartsWith(@"../"))
    .Count();
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

Actually, you can do it in a really LINQy (and awkward :) ) way like this:

private static int CountPatternAppearancesInString(string str, string pattern)
{
    var count = str
        .Select(
            (_, index) =>
                index < str.Length - pattern.Length + 1 &&
                str.Skip(index)
                    .Take(pattern.Length)
                    .Zip(pattern, (strChar, patternChar) => strChar == patternChar)
                    .All(areEqual => areEqual))
        .Count(isMatch => isMatch);

    return count;
}

Or, using some of the String-provided methods:

private static int CountPatternAppearancesInString(string str, string pattern)
{
    var count = str
        .Select(
            (_, index) =>
                index < str.Length - pattern.Length + 1 &&
                str.IndexOf(pattern, index, pattern.Length) >= 0)
        .Count(isMatch => isMatch);

    return count;
}

But, as already said, it is suboptimal and serves for illustration purpose only.

galenus
  • 2,087
  • 16
  • 24