java linq表达式_如何使用LINQ选择具有最小或最大属性值的对象

我有一个具有Nullable DateOfBirth属性的Person对象。 有没有一种方法可以使用LINQ来查询Person对象列表中最早/最小的DateOfBirth值。

这是我开始的:

var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));

空的DateOfBirth值设置为DateTime.MaxValue以便将它们排除在Min考虑之外(假设至少一个具有指定的DOB)。

但是对我来说,所有要做的就是将firstBornDate设置为DateTime值。 我想要得到的是与此匹配的Person对象。 我是否需要这样编写第二个查询:

var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);

还是有一种更精简的方法?

#1楼

public class Foo {

public int bar;

public int stuff;

};

void Main()

{

List fooList = new List(){

new Foo(){bar=1,stuff=2},

new Foo(){bar=3,stuff=4},

new Foo(){bar=2,stuff=3}};

Foo result = fooList.Aggregate((u,v) => u.bar < v.bar ? u: v);

result.Dump();

}

#2楼

无需额外包装的解决方案:

var min = lst.OrderBy(i => i.StartDate).FirstOrDefault();

var max = lst.OrderBy(i => i.StartDate).LastOrDefault();

您也可以将其包装到扩展中:

public static class LinqExtensions

{

public static T MinBy(this IEnumerable source, Func propSelector)

{

return source.OrderBy(propSelector).FirstOrDefault();

}

public static T MaxBy(this IEnumerable source, Func propSelector)

{

return source.OrderBy(propSelector).LastOrDefault();

}

}

在这种情况下:

var min = lst.MinBy(i => i.StartDate);

var max = lst.MaxBy(i => i.StartDate);

顺便说一下... O(n ^ 2)不是最佳解决方案。 保罗·贝茨 ( Paul Betts)提供了比我强的解决方案。 但是我仍然是LINQ解决方案,比这里的其他解决方案更简单,更短。

#3楼

我一直在寻找类似的东西,最好不要使用库或对整个列表进行排序。 我的解决方案最终类似于问题本身,只是简化了一点。

var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == People.Min(p2 => p2.DateOfBirth));

#4楼

以下是更通用的解决方案。 它基本上执行相同的操作(以O(N)顺序),但是在任何IEnumberable类型上,并且可以与属性选择器可以返回null的类型混合使用。

public static class LinqExtensions

{

public static T MinBy(this IEnumerable source, Func selector)

{

if (source == null)

{

throw new ArgumentNullException(nameof(source));

}

if (selector == null)

{

throw new ArgumentNullException(nameof(selector));

}

return source.Aggregate((min, cur) =>

{

if (min == null)

{

return cur;

}

var minComparer = selector(min);

if (minComparer == null)

{

return cur;

}

var curComparer = selector(cur);

if (curComparer == null)

{

return min;

}

return minComparer.CompareTo(curComparer) > 0 ? cur : min;

});

}

}

测试:

var nullableInts = new int?[] {5, null, 1, 4, 0, 3, null, 1};

Assert.AreEqual(0, nullableInts.MinBy(i => i));//should pass

#5楼

因此,您要使用ArgMin或ArgMax 。 C#没有针对这些的内置API。

我一直在寻找一种干净有效的方法(及时(O [n))来做到这一点。 我想我发现了一个:

此模式的一般形式是:

var min = data.Select(x => (key(x), x)).Min().Item2;

^ ^ ^

the sorting key | take the associated original item

Min by key(.)

特别地,使用原始问题中的示例:

对于支持值元组的 C#7.0及更高版本:

var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;

对于7.0之前的C#版本,可以改用匿名类型 :

var youngest = people.Select(p => new { ppl = p; age = p.DateOfBirth }).Min().ppl;

之所以起作用,是因为值元组和匿名类型都具有明智的默认比较器:对于(x1,y1)和(x2,y2),它首先比较x1与x2 ,然后比较y1与y2 。 这就是为什么可以在这些类型上使用内置的.Min的原因。

而且由于匿名类型和值元组都是值类型,因此它们都应该非常有效。

注意

在上面的ArgMin实现中,为简单起见,我假定DateOfBirth类型为DateTime 。 原始问题要求排除那些具有空DateOfBirth字段的条目:

空的DateOfBirth值设置为DateTime.MaxValue以便将它们排除在Min考虑之外(假设至少一个具有指定的DOB)。

可以通过预过滤来实现

people.Where(p => p.DateOfBirth.HasValue)

因此,对于实现ArgMin或ArgMax的问题ArgMax 。

笔记2

上面的方法有一个告诫,当有两个具有相同最小值的实例时, Min()实现将尝试将这些实例作为平局决胜。 但是,如果实例的类未实现IComparable ,则将引发运行时错误:

至少一个对象必须实现IComparable

幸运的是,这仍然可以解决得很干净。 这个想法是将一个明显的“ ID”与充当明确的决胜局的每个条目相关联。 我们可以为每个条目使用增量ID。 仍以人口年龄为例:

var youngest = Enumerable.Range(0, int.MaxValue)

.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值