从 Linq 的列表中选择多个字段

Posted

技术标签:

【中文标题】从 Linq 的列表中选择多个字段【英文标题】:Select Multiple Fields from List in Linq 【发布时间】:2010-11-15 05:40:11 【问题描述】:

在 ASP.NET C# 中,我有一个结构:

public struct Data

    public int item1;
    public int item2;
    public int category_id;
    public string category_name;

我有一份清单。我想选择category_idcategory_name,运行DISTINCT,最后在category_name 上运行ORDERBY

这是我现在拥有的:

List<Data> listObject = getData();
string[] catNames = listObject
                    .Select(i=> i.category_name)
                    .Distinct()
                    .OrderByDescending(s => s)
                    .ToArray();

这显然只是获取类别名称。我的问题是,我如何获得多个字段,我将把它存储在什么数据结构中(不是string[])?

编辑

使用结构列表并不是一成不变的。如果建议更改我的支持数据结构以使选择更容易(我会写很多这样的),那么我很乐意接受建议。

【问题讨论】:

虽然它与 LINQ 端无关,但我会强烈建议您不要使用可变结构或公共字段。就我个人而言,我一开始很少创建结构体,但可变结构体只是自找麻烦。 @Jon Skeet 谢谢。我会将其转换为具有私人成员的常规课程。 @Midhat:可变结构会导致各种问题,因为它们的行为与人们期望的不同。并且公共字段完全缺乏封装。 @Jon Skeet。您能否更具体地了解可变结构的陷阱,或者指出我的阅读内容。 @Midhat:以***.com/questions/441309/why-are-mutable-structs-evil 为起点。 【参考方案1】:

匿名类型允许您将任意字段选择到稍后在代码中强类型化的数据结构中:

var cats = listObject
    .Select(i => new  i.category_id, i.category_name )
    .Distinct()
    .OrderByDescending(i => i.category_name)
    .ToArray();

由于您(显然)需要存储它以供以后使用,您可以使用 GroupBy 运算符:

Data[] cats = listObject
    .GroupBy(i => new  i.category_id, i.category_name )
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();

【讨论】:

我想在 1 列上使用 distinct 并检索多个列。那么我该怎么做呢? 我从来没想过要把Select变成一个新类型。就我而言,我选择了一个新的KeyValuePair【参考方案2】:
var selectedCategories =
    from value in
        (from data in listObject
        orderby data.category_name descending
        select new  ID = data.category_id, Name = data.category_name )
    group value by value.Name into g
    select g.First();

foreach (var category in selectedCategories) Console.WriteLine(category);

编辑:让它更 LINQ-ey!

【讨论】:

【参考方案3】:

你可以使用匿名类型:

.Select(i => new  i.name, i.category_name )

编译器将为具有namecategory_name 属性的类生成代码并返回该类的实例。您也可以手动指定属性名称:

i => new  Id = i.category_id, Name = i.category_name 

您可以拥有任意数量的属性。

【讨论】:

【参考方案4】:

您可以使用 linq Select 选择多个字段,如上图所示的各种示例,这将作为匿名类型返回。如果你想避免这种匿名类型,这是一个简单的技巧。

var items = listObject.Select(f => new List<int>()  f.Item1, f.Item2 ).SelectMany(item => item).Distinct();

我认为这可以解决您的问题

【讨论】:

【参考方案5】:

这是anonymous types 非常适合的任务。您可以返回由编译器自动创建的类型的对象,根据使用情况推断。

语法是这样的:

new  Property1 = value1, Property2 = value2, ... 

对于您的情况,请尝试以下操作:

var listObject = getData();
var catNames = listObject.Select(i =>
    new  CatName = i.category_name, Item1 = i.item1, Item2 = i.item2 )
    .Distinct().OrderByDescending(s => s).ToArray();

【讨论】:

关于如何命名输出属性的额外说明非常好,谢谢!【参考方案6】:

您可以将其设为 KeyValuePair,因此它将返回 "IEnumerable&lt;KeyValuePair&lt;string, string&gt;&gt;"

所以,它会是这样的:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();

【讨论】:

这增加了不必要的复杂性而没有任何好处【参考方案7】:
var result = listObject.Select( i => new i.category_name, i.category_id  )

这使用匿名类型,因此您必须使用 var 关键字,因为事先不知道表达式的结果类型。

【讨论】:

【参考方案8】:
(from i in list
 select new  i.category_id, i.category_name )
 .Distinct()
 .OrderBy(i => i.category_name);

【讨论】:

【参考方案9】:
public class Student

    public string Name  set; get; 
    public int ID  set; get; 


class Program

  static void Main(string[] args)
    
        Student[] students =
        
        new Student  Name="zoyeb" , ID=1,
        new Student  Name="Siddiq" , ID=2,
        new Student  Name="sam" , ID=3,
        new Student  Name="james" , ID=4,
        new Student  Name="sonia" , ID=5
        ;

        var studentCollection = from s in students select new  s.ID , s.Name;

        foreach (var student in studentCollection)
        
            Console.WriteLine(student.Name);
            Console.WriteLine(student.ID);
        
    

【讨论】:

【参考方案10】:

给定List&lt;MyType1&gt; internalUsersList&lt;MyType2&gt; externalUsers,基于电子邮件地址的共享密钥...


对于 C# 7.0+:

var matches = (
    from i in internalUsers
    join e in externalUsers
    on i.EmailAddress.ToUpperInvariant() equals e.Email.ToUpperInvariant()
    select (internalUser:i, externalUser:e)
).ToList();

这会给你matches 作为List&lt;(MyType1, MyType2)&gt;

如果您愿意,可以从那里比较它们:

var internal_in_external = matches.Select(m => m.internalUser).ToList();
var external_in_internal = matches.Select(m => m.externalUser).ToList();

var internal_notIn_external = internalUsers.Except(internal_in_external).ToList();
var external_notIn_internal = externalUsers.Except(external_in_internal).ToList();

internal_in_externalinternal_notIn_external 将是 List&lt;MyType1&gt; 类型。

external_in_internalexternal_notIn_internal 将是 List&lt;MyType2&gt; 类型


对于 7.0 之前的 C# 版本:

var matches = (
    from i in internalUsers
    join e in externalUsers
    on i.EmailAddress.ToUpperInvariant() equals e.Email.ToUpperInvariant()
    select new Tuple<MyType1, MyType2>(i, e)
).ToList();

这会给你matches 作为List&lt;Tuple&lt;MyType1, MyType2&gt;&gt;

如果您愿意,可以从那里比较它们:

var internal_in_external = matches.Select(m => m.Item1).ToList();
var external_in_internal = matches.Select(m => m.Item2).ToList();

var internal_notIn_external = internalUsers.Except(internal_in_external).ToList();
var external_notIn_internal = externalUsers.Except(external_in_internal).ToList();

internal_in_externalinternal_notIn_external 将是 List&lt;MyType1&gt; 类型。

external_in_internalexternal_notIn_internal 将是 List&lt;MyType2&gt; 类型

【讨论】:

以上是关于从 Linq 的列表中选择多个字段的主要内容,如果未能解决你的问题,请参考以下文章

Linq to SQL 选择 DateTime 字段在任何日期范围列表中的行

选择多个字段分组并求和

如何使用 LINQ 从列表中选择提供的索引范围内的值

Linq 到实体 Skip() 和 Take()

C# LINQ 从数组/列表中不包含值的位置选择

C# LINQ 从字符串数组列表中选择