我想知道是否可以为Linq扩展(或自定义扩展)编写表达式,以使用比较集合的两个元素的lambda表达式来过滤集合。
换句话说,如果我有一个List<DateTime>
和一些值var v = DateTime.Today
,那么我想知道是否可以编写创建一个方法来返回该集合的第一个元素小于或等于值current <= v
,其中集合的下一个元素大于或等于值next >= v
。
请注意,以上只是一个示例,可能是也可能不是最终实施。
以下是一个有效的解决方案,.First()
方法接受Func<DateTime, DateTime, bool>
方法,其中两个DateTime
参数是序列的连续元素:
dateCollection.First((current, next) => current <= v && next >= v);
请注意,根据给出的示例,有效的解决方法可能是使用.OrderBy
,然后找到大于d
的第一个索引并减去1
。但是,这种比较并不是我唯一需要的比较。我可能会遇到这样的情况,即我List<string>
检查current
元素的第一个字母是v
的第一个字母,next
和v
element以我的值public static T First (...)
{
...
}
的最后一个字母结尾。
我正在寻找一些只是代码的东西。我的目标是找到最简单的解决方案,因此简洁性很重要。
我正在寻找的是以下形式:
.First()
我相信这也需要两个或更多lambda表达式作为参数。可能提供良好解决方案的一件事是能够选择序列中所有可能的,连续的元素对,并在其上调用//value
var v = 5;
//if my collection is the following
List<int> stuff = { a, b, c, d };
//select into consecutive pairs, giving:
var pairs = ... // { { a, b }, { b, c }, { c, d } };
//then run comparison
pairs.First(p => p[0] <= v && p[1] >= v).Select(p => p[0]);
方法。
例如:
{{1}}
谢谢,快乐的编码! :)
答案 0 :(得分:3)
List<int> list = new List<int>();
list.Add(3);
list.Add(2);
list.Add(8);
list.Add(1);
list.Add(4);
var element = list
.Where((elem, idx) => idx < list.Count-1 && elem<=list[idx+1])
.FirstOrDefault();
Console.WriteLine(element);
rsult:2
其中'elem'是当前元素,'idx'是当前元素的索引
答案 1 :(得分:2)
我们可以创建的是Pairwise
方法,它可以将一系列值映射到表示每个值及其前面的值的对序列中。
public static IEnumerable<Tuple<T, T>> Pairwise<T>(this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
T prev = iterator.Current;
while (iterator.MoveNext())
{
yield return Tuple.Create(prev, iterator.Current);
prev = iterator.Current;
}
}
}
现在我们可以写出:
var item = data.Pairwise()
.First(pair => pair.Item1 <= v && pair.Item2 >= v)
.Item1;
如果您要使用相当多的内容,则可能需要创建一个新的自定义类型来替换Tuple
,以便您可以拥有Current
和{{1 }}属性,而不是Next
和Item1
。
答案 2 :(得分:1)
严格遵循描述,您可以组合linq和list索引来查找与您的标准匹配的第一个索引,并返回其元素:
DateTime d= DateTime.Today;
var res = dateCollection[Enumerable.Range(0, dateCollection.Count - 1).First(i => dateCollection[i] <= d && dateCollection[i + 1] >= d)];
答案 3 :(得分:1)
不确定你想要返回的是我的主要问题,但是以你的例子并将其放入LINQ语句中,它将是这样的:
DateTime? Firstmatch = dateCollection.DefaultIfEmpty(null).FirstOrDefault(a => a <= d && ((dateCollection.IndexOf(a) + 1) < (dateCollection.Count) && dateCollection[dateCollection.IndexOf(a) + 1] >= d));
答案 4 :(得分:0)
Servy的答案可以在没有扩展方法的情况下处理:
var first = items
.Select((current, index) => index > 0 ? new { Prev = items.ElementAt(index-1), Current = current } : null)
.First(pair => pair != null && pair.Prev <= v && pair.Current >= v)
.Prev;