简体   繁体   中英

How to determine if any element in a collection appears in a string with LINQ?

I have Dictionary<string, List<string>> object. The Key represents the name of a file and the Value is a List<string> which represents the names of certain methods in the file.

I loop over the dictionary and use the Key to read in data from the file. I'm trying to then find lines in this file which contain an element from the Values object:

static void FindInvalidAttributes(Dictionary<string, List<string>> dictionary)
{
    //Get the files from my controller dir
    List<string> controllers = Directory.GetFiles(controllerPath, "*.cs", SearchOption.AllDirectories).ToList<string>();

    //Iterate over my dictionary
    foreach (KeyValuePair<string, List<string>> entry in dictionary)
    {
        //Build the correct file name using the dictionary key
        string controller = Path.Combine(ControllerPath, entry.Key + "Controller.cs");

        if (File.Exists(controller))
        {
            //Read the file content and loop over it
            string[] lines = File.ReadAllLines(controller);
            for (int i = 0; i < lines.Count(); i++)
            {
                //loop over every element in my dictionary's value (List<string>)
                foreach (string method in entry.Value)
                {
                    //If the line in the file contains a dictionary value element
                    if (lines[i].IndexOf(method) > -1 && lines[i].IndexOf("public") > -1)
                    {
                        //Get the previous line containing the attribute
                        string verb = lines[i - 1];
                    }
                }
            }
        }
    }
}

There's got to be a cleaner way to implement the code inside the if (File.Exists(controller)) statement. I don't want to nest a foreach inside of a for , inside the parent-most foreach .

Question: How can I determine if a string contains any element in a List<string> using LINQ?

Note that the two values are not identical; part of the string should contain the entire list element. I was able to find plenty of examples looking for the string inside the list elements, but that is not what I'm trying to do.

Example:

lines[0] = "public void SomeMethod()";
lines[1] = "public void SomeOtherMethod()";

List<string> myList = new List<string>();
myList.Add("SomeMethod");
myList.Add("AnotherMethod");

Using the above data, lines[0] should result in my FindInvalidAttributes method looking at the previous line because this string contains the first element in myList . lines[1] should not result in the method inspecting the previous line because SomeOtherMethod does not appear in myList .

Edit I'm very curious why this was downvoted and marked to be closed for being "too broad". I'm asking a tremendously specific question, provided my code, sample data and expected output for the sample data.

If you have lines with all lines from the file and entry.Value as the list of words to look for, then the following will return all lines containing at least one word from entry.Value :

var result = lines.Where(l => l.Split(' ').Intersect(entry.Value).Any());

or more into your specific example:

var result = 
    lines.Where(l => 
        l.Trim().StartsWith("public") && entry.Value.Any(l.Contains));

You can build up a regular expression that looks for all items in the list for the file in one shot.

Also, note that you're not using controllers variable at all.

static void FindInvalidAttributes(Dictionary<string, List<string>> dictionary)
{
    //Get the files from my controller dir
    List<string> controllers = Directory.GetFiles(controllerPath, "*.cs", SearchOption.AllDirectories).ToList<string>();

    //Iterate over my dictionary
    foreach (var entry in dictionary)
    {
        //Build the correct file name using the dictionary key
        string controller = Path.Combine(ControllerPath, entry.Key + "Controller.cs");

        if (File.Exists(controller))
        {
            var regexText = "(?<attribLine>.*)\n" + string.Join("|", entry.Value.Select(t => "[^\n]*public\s[^\n]*" + Regex.Escape(t)))
            var regex = new Regex(regexText)
            //Read the file content and loop over it
            var fileContent = File.ReadAllText(controller);
            foreach (Match match in regex.Matches(fileContent))
            {
                 // Here, match.Groups["attribLine"] should contain here what you're looking for.
            }
        }
    }
}

I don't have input, so cannot easily test the code or the regex, but it should give the general idea for the approach and simplification.

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