简体   繁体   中英

What is the best way to see if Dictionary string values contain some pattern and at the same time doesn't contain another in C#?

Let's say I have a dictionary Dictionary<string,sting>.

I want to see if the string values of this dictionary contain some string pattern, let's say "abc" and at the same time doesn't contain patterns "def" and "ghi". Also, I want this check to be not case sensitive.

I can write it like this:

var options = new Dictionary<string,string>();
        ...     
if (!options.Any(kvp1 => (kvp1.Value.Contains("def", StringComparison.InvariantCultureIgnoreCase))
&& !options.Any(kvp2 => kvp2.Value.Contains("ghi", StringComparison.InvariantCultureIgnoreCase))
&& options.Any(kvp3 => kvp3.Value.Contains("abc", StringComparison.InvariantCultureIgnoreCase))                               ))
        {
        Do Something...
        }

So I wonder, is there more elegant way to perform such operation?

UPD: This code sure has a bug. What I desire is to check that values list has at least one element that contains "abc", and no elements at all that has "def" and "ghi". So edited code a bit.

try this

var options = new Dictionary<string, string>();
var includedValues = new[] { "abc", "dfg" };
var excludedValues = new[] { "hij", "klm" };

var cultureInf = CultureInfo.CurrentCulture;
var containsPredicate = new Func<string, string, bool>((s1, s2) => {
    return cultureInf.CompareInfo.IndexOf(s1, s2, CompareOptions.IgnoreCase)>=0;
}); 

if (
includedValues.All(v => options.Values.Any(kv =>containsPredicate(kv, v)))
&&
excludedValues.All(v => !options.Values.Any(kv =>containsPredicate(kv, v))))
{
    // do smthg
}

There are multiple things to take care of. Culture-based case-insensitive comparison and dynamic, non hard-coded patterns for example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;

namespace ConsoleApplication7
{
  internal static class Program
  {
    internal static void Main(string[] args)
    {
      var options = new Dictionary<string, string>();

      var compareCulture = CultureInfo.CurrentCulture;
      var patternsNeeded = new[] { "abc", "def" };
      var patternsForbidden = new[] { "ghi", "xxx" };

      Func<KeyValuePair<string, string>, bool> meetsCriteria =
        kvp => patternsNeeded.Any(p => compareCulture.CompareInfo.IndexOf(kvp.Value, p, CompareOptions.IgnoreCase) >= 0)
           && !patternsForbidden.Any(p => compareCulture.CompareInfo.IndexOf(kvp.Value, p, CompareOptions.IgnoreCase) >= 0);

      var dictionaryContainsHits = options.Any(meetsCriteria);
    }
  }
}

If your dictionary is large, you may want to throw in an .AsParallel() here and there. As those are all read operations, it should work fine.

Not sure if you want .Any() or .All() for the patterns that are in your positive list. Take the one that fits your use case.

You can directly access values via Values property on the dictionary object.

Well, you can create your own linq to do this approach:

public static class ExtendedLinq
{
    public static bool All<T>(this IEnumerable<T> source, params Func<T, bool>[] conditions)
    {
        foreach (var condition in conditions)
            if (!source.Any(condition))
                return false;

        return true;
    }
}

and then usage:

var options = new Dictionary<string, string>();
if (options.Values.All((t => t.Contains("abc")), (t => !t.Contains("def")), (t => !t.Contains("ghi"))))
{
    // Do something
}

Hope its finally what you wanted.

Btw, that contains which is case insensitive is not part of the framework, so I didnt include it, since you already have it.

old (misunderstood the question): var options = new Dictionary(); if(options.Values.Any(t => t.Contains("abc") && !t.Contains("def") && !t.Contains("ghi"))) { // Do something }

It depends on the context, because it might be usefull to take a look on the Sorted dictionary or Regex classes.

i would suggest that you do this :

 var dictionaryVal = d.Select(p => p.Value.ToLower());
 string string1 = "string1";
 string string2 = "string2";
 string string3 = "string3";

 if (dictionaryVal.Contains(string1.ToLower())&&dictionaryVal.Contains(string2.ToLower())&&dictionaryVal.Contains(string3.ToLower()))
 {
     //your code here
 }

that is to see if a dictionary's values contain string1 but does not contain string2 and string3.however...
if you like to check for a single dictionary item and check all 3 strings against the same item's value it would be :

if (d.Any(p => p.Value.Contains(string1.ToLower()) && !p.Value.Contains(string2.ToLower()) && !p.Value.Contains(string3.ToLower())))
{
  // your code here
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM