如何使用LINQ查询字符串的子集

本文关键字:子集 字符串 查询 何使用 LINQ | 更新日期: 2023-09-27 17:58:42

假设我有一个名为letters:的无序List<String>

letters.Add("d.pdf");
letters.Add("a.pdf");
letters.Add("c.pdf");
letters.Add("b.pdf");
letters.Add("e.pdf");
letters.Add("f.pdf");

我想按字母顺序排列该列表,然后取bd之间的元素(包括端点),这样会返回一个类似{"b.pdf","c.pdf","d.pdf"}的新列表。

如何使用LINQ查询字符串的子集

简单的foreach循环解决方案:

var letters = new List<string> {"d", "a", "c", "b", "e"};
            letters.Sort();
            var start = "b";
            var end = "d";
            var r = new List<string>();
            foreach (var l in letters)
            {
                if (l.CompareTo(start) >= 0 && l.CompareTo(end) <= 0)
                {
                    r.Add(l);
                }
            }

另一个简单的解决方案:

var letters = new List<string> {"d", "a", "c", "b", "e"};
letters.Sort();
 var startIndex = letters.IndexOf(letters.Find(l=> l.CompareTo(start) == 0));
 var endIndex = letters.IndexOf(letters.Find(l => l.CompareTo(end) == 0));
 var r = letters.GetRange(startIndex, endIndex - startIndex + 1);

如果您使用TakeWhile,这个解决方案应该可以工作,这更像Linq。主要问题是将起始元素放在集合的第一个索引中,否则TakeWhile将不起作用。

    var letters = new List<string> { "d", "a", "c", "b", "e" };
    letters.Sort();
    var start = "b";
    var end = "d";
    var startIndex = letters.IndexOf(letters.Find(l=> l.CompareTo(start) == 0));
    var r = letters.GetRange(startIndex, letters.Count - startIndex)
              .TakeWhile(l => l.CompareTo(start) >= 0 && l.CompareTo(end) <= 0).ToList();

我不确定是否有内置的东西可以轻松支持这一点,但添加了一个新的扩展方法,你就可以很容易地做到这一点:

void Main()
{
    var letters = new List<string>();
    letters.Add("d.pdf");
    letters.Add("a.pdf");
    letters.Add("c.pdf");
    letters.Add("b.pdf");
    letters.Add("e.pdf");
    var results = letters
        .OrderBy(x => x)
        .SkipWhile(x => x != "b.pdf")
        .TakeTo(x => x == "d.pdf")
        .ToList();
}
static class Extensions
{
    public static IEnumerable<TValue> TakeTo<TValue>(this IEnumerable<TValue> source, Func<TValue, bool> predicate)
    {
        bool predicateReached = false;
        foreach (var value in source)
        {
            yield return value;         
            predicateReached = predicate(value);
            if (predicateReached) yield break;
        }
    }
}

TakeTo扩展方法的工作原理与TakeWhile扩展类似,只是它将生成所有值,直到并包括与给定谓词函数匹配的第一个值。

这应该适用于您的情况:

letters.OrderBy(x => x).TakeWhile(x => x != "e.pdf");

如果你想推广它,你可以写一个扩展方法:

public static IEnumerable<TSource> GetRange<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> start,
        Func<TSource, bool> end)
{
     int counter = 0;
     foreach (var item in source)
     {
         if (start(item) || end(item))
         {
              yield return item;
              counter++;
              if(counter == 2) yield break;
         }else if (counter != 0) yield return item;
     }
} 

然后使用它:

letters.OrderBy(x => x).GetRange(x => x == "b.pdf", x => x == "d.pdf");

或者你可以在不使用LINQ的情况下做到这一点,List<T>还有一个GetRange方法:

letters.Sort();
int startIndex = letters.IndexOf("b.pdf");
int endIndex = letters.IndexOf("d.pdf");
var result = letters.GetRange(startIndex, endIndex - startIndex +1);