1

I am using Regular Expression for matching patterns say example in the follwoing example i am matching string to count vowels.

void VowelsCountInEachWord()
{
  Regex rx = new Regex("[aeiou]");
  var words=new string[] 
                         {"aesthetic", "benevolent", "abstract",
                           "capricious", "complacent", "conciliatory",
                           "devious", "diligent", "discernible","dogmatic",
                           "eccentric","fallacious","indifferent","inquisitive",
                           "meticulous","pertinent","plausible", "reticent"
                         };

  var filter = from w in words where (rx.IsMatch(w.ToLower())) select new  

              {w,count=VowelsCounting(w)};


 foreach (var v in filter)
 {
     Console.WriteLine("String {0} contains {1} vowels", v.w, v.count);
 }

}

 public int VowelsCounting(string value)
 {

    int cnt=0;
    foreach (char c in value)
    {
         switch (c)
         {
              case 'a':cnt++;break;
              case 'e':cnt++;break;
              case 'i':cnt++;break;
              case 'o':cnt++;break;
              case 'u':cnt++;break;
           }
     }
            return cnt++;
  }

1) Without using regular expression does C# offer any construct for matching pattern?

2) For Counting individual character against the string do i need to derive my own method?

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
user190560
  • 3,459
  • 4
  • 20
  • 15

4 Answers4

3

Your solution is not bad, but if you insist:

var vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
var words = new string[] 
             {"aesthetic", "benevolent", "abstract",
               "capricious", "complacent", "conciliatory",
               "devious", "diligent", "discernible","dogmatic",
               "eccentric","fallacious","indifferent","inquisitive",
               "meticulous","pertinent","plausible", "reticent"
             };

var filter =
    (from w in words
    select new { w, count = w.ToLower().Count(c => vowels.Contains(c)) }).
        Where(item => item.count > 0);

Edit: As some suggested here, I removed the ToCharArray, I added ToLower check & also a zero vowels filter.

HuBeZa
  • 4,715
  • 3
  • 36
  • 58
3

1) Without using regular expression does C# offer any construct for matching pattern?

Nothing as powerful as regex that gives everything to you in one shot.

2) For Counting individual character against the string do i need to derive my own method?

I do not recommend this approach, but I'm stating it just to show that you could use some built in method to achieve it. You could conceivably use String.IndexOf and look for "a" starting from the 0 index, and keep chugging along in a loop while incrementing a counter on positive matches. Then repeat for "e"..."u" but it will be much less efficient than a regex or a for loop.

A better approach would be to just loop over the string char by char and either feed it to your existing switch statement or look it up in some collection.

Since you want to use LINQ, here's how the above for loop idea could be rewritten to fit. Note, this idea is similar to HuBeZa's solution so +1 there. However, I use a list for the look up and use the StringComparison.InvariantCultureIgnoreCase enumeration to ignore case:

var vowels = new List<string> { "a", "e", "i", "o", "u" };
var query = words.Select(s => new
            {
                Text = s,
                Count = s.Count(c => vowels.Exists(vowel => 
                    vowel.Equals(c.ToString(), 
                        StringComparison.InvariantCultureIgnoreCase)))
            });
foreach (var item in query)
{
    Console.WriteLine("String {0} contains {1} vowels", item.Text, item.Count);
}

My original regex response is below.


Regex Approach

There's a better regex solution than the one you're using. I'm not sure if you're aware of it so I felt it warranted a post. In question #1 you said "without using regular expressions," but IMHO that directly conflicts with question #2 where you asked if you had to derive your own method.

You can shorten your code by using the Regex.Matches method and the Count property on the returned MatchCollection:

Regex rx = new Regex("[aeiou]");
// to ignore case use: new Regex("[aeiou]", RegexOptions.IgnoreCase);
string[] words =
{
    "aesthetic", "benevolent", "abstract",
    "capricious", "complacent", "conciliatory",
    "devious", "diligent", "discernible","dogmatic",
    "eccentric","fallacious","indifferent","inquisitive",
    "meticulous","pertinent","plausible", "reticent"
};

foreach (string input in words)
{
    Console.WriteLine("String {0} contains {1} vowels",
        input, rx.Matches(input).Count);
}

// if you really want to use LINQ
var query = from s in words
            select new
            {
                Text = s,
                Count = rx.Matches(s).Count
            };
foreach (var item in query)
{
    Console.WriteLine("String {0} contains {1} vowels", item.Text, item.Count);
}

BTW, you can further shorten your original code by changing 2 items:

1) The string array declaration (I've done this in my example above)
2) Make your case statements fall through to the next case label:

switch (c)
{
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        cnt++;
        break;
}

EDIT: updated with a LINQ query. It's not much different than what the OP had, just using the Matches approach.

Community
  • 1
  • 1
Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • Good points on syntax shortening. You can also drop the foreach by using LINQ Select method: words.Select(input => new { input, Count = rx.Matches(input).Count }); – HuBeZa Oct 18 '09 at 16:10
  • @HuBeZa: thanks! I updated my code with a similar LINQ query just as you were commenting :) Yours is more compact using lamda query of course; I was matching the OP's comprehension query. – Ahmad Mageed Oct 18 '09 at 16:13
  • +0.5 for the most detailed answer, and another +0.5 for **ignore case comparison** – HuBeZa Oct 19 '09 at 06:28
2

Following solution will only include items if it contains vowels and also it will be the fastest solution.

var filter = from w2 in
                 (from w in words
                  select new
                  {
                      w,
                      count =
                          w.ToLower().ToCharArray().Count(
                            c => c == 'a' || 
                                c == 'e' || 
                                c == 'i' || 
                                c == 'o' || 
                                c == 'u')
                  })
             where (w2.count > 0)
             select w2; 
Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • 2
    Nice, but the call to `ToCharArray` is not necessary because `string` implements `IEnumerable`. However, the `IEnumerable` extension methods are not exposed in Visual Studio for dubious reasons: http://stackoverflow.com/questions/345883/why-doesnt-vs-2008-display-extension-methods-in-intellisense-for-string-class – JulianR Oct 18 '09 at 15:41
  • @JulianR , Oh i saw that later on, thought VS hided it so it didnt exist :) – Akash Kava Oct 18 '09 at 15:45
  • @JulianR: Thank you for the enlightening link. Dubious is the exact description for this "feature". – HuBeZa Oct 18 '09 at 15:56
  • PS: This method can be made 4x faster by removing the `ToLower` and simply add the comparisons for upper case. – JulianR Oct 18 '09 at 17:51
  • Thanks Julian I am just trying to show the way I guess the requester should do little more work for himself coz I am not going to be paid for anything :) – Akash Kava Oct 18 '09 at 18:04
1

Everyone is making this far too complex:

Regex rx = new Regex("[aeiou]", RegexOptions.IgnoreCase);

var query = from w in words
            let vowelCount = rx.Matches(w).Count
            where vowelCount != 0
            select new { w, vowelCount };

Of course if you don't need a list of the words that have vowels (what words don't?) and all you want to do is output the counts, then it's easier to just use a loop.

Joren
  • 14,472
  • 3
  • 50
  • 54
  • Not everyone... this is the approach I used in my response: http://stackoverflow.com/questions/1585085/linq-pattern-matching/1585240#1585240 – Ahmad Mageed Oct 18 '09 at 16:24
  • 1
    No ,not eveyone is making this complex. I asked the question,without regular expression ,can i process it.That is whyy they are giving answers. Thanks you ver much Joren. – user190560 Oct 18 '09 at 16:25
  • Oh, you're right Ahmad, I had missed your post for some reason. – Joren Oct 18 '09 at 16:32