使用Linq进行分区

时间:2018-09-18 21:18:54

标签: c# list linq

这是我的问题,我有一个查询结果集,如下所示:

id_1  - 0 - datetime - gps coordinates  
id_2  - 0 - datetime - gps coordinates
id_3  - 1 - datetime - gps coordinates 
id_4  - 1 - datetime - gps coordinates 
id_5  - 1 - datetime - gps coordinates
id_6  - 1 - datetime - gps coordinates 
id_7  - 0 - datetime - gps coordinates 
id_8  - 0 - datetime - gps coordinates 
id_9  - 0 - datetime - gps coordinates 
id_10 - 0 - datetime - gps coordinates
id_11 - 1 - datetime - gps coordinates 
id_12 - 1 - datetime - gps coordinates 
id_13 - 0 - datetime - gps coordinates 
id_14 - 0 - datetime - gps coordinates

这显然会返回一个列表,但是我想将第二列中值为1的集合保存在列表列表中,其中每组列表都是以前具有0的列表。 看起来像这样:

List1: [id_3], [...] , [id_6]
List2: [id_11], [...], [id_12]

我不知道0或1的元素数量,因此在这种意义上必须是通用的

我正在使用C#4.5,并且正在考虑使用Linq来实现此目的,而不是老式的foreach。

有人能指出我正确的方向吗?

3 个答案:

答案 0 :(得分:2)

我认为框架中没有内置的东西,但是您可以为其创建扩展方法:

public static class LinqHelper
{
    public static IEnumerable<List<Item>> Partition(this IEnumerable<Item> source, Func<Item, bool> selector)
    {
        List<Item> currentList = new List<Item>();

        foreach (var item in source)
        {
            if (selector(item))
            {
                currentList.Add(item);
            }
            else if (currentList.Count != 0)
            {
                yield return currentList;
                currentList = new List<Item>();
            }
        }

        if (currentList.Count != 0)
        {
            yield return currentList;
        }
    }
}

public class Item
{
    public int Id { get; set; }
    public int Val { get; set; }
}

void Main()
{
    var list = new List<Item>(){
        new Item{ Id = 1, Val = 0 },
        new Item{ Id = 2, Val = 0 },
        new Item{ Id = 3, Val = 1 },
        new Item{ Id = 4, Val = 1 },
        new Item{ Id = 5, Val = 1 },
        new Item{ Id = 6, Val = 1 },
        new Item{ Id = 7, Val = 0 },
        new Item{ Id = 8, Val = 0 },
        new Item{ Id = 9, Val = 0 },
        new Item{ Id = 10, Val = 0 },
        new Item{ Id = 11, Val = 1 },
        new Item{ Id = 12, Val = 1 },
        new Item{ Id = 13, Val = 0 },
        new Item{ Id = 14, Val = 0 },
    };

    var result = list.Partition(i => i.Val == 1).Where(i => true).ToList();
}

答案 1 :(得分:2)

如果您希望避免使用foreach,可以使用LINQ的Aggregate扩展名来实现。

提供以下课程:

public class SomeType 
{
    public int Id {get;set;}
    public int Val {get;set;}
}

并已生成以下项目:

var items = new List<SomeType>();
for(var i = 1; i <= 14; i++) 
{
    var val = 0;
    if((3 <= i && i <= 6) || (11 <= i && i <= 12))
        val = 1;
    items.Add(new SomeType { Id = i, Val = val});
}

您可以像这样获得值为1的项目列表:

var grouped = items.Aggregate(new List<List<SomeType>>() { new List<SomeType>() },
    (acc,elem) => 
    { 
        if(elem.Val == 0 && acc.Last().Count != 0)  
            acc.Add(new List<SomeType>());
        else if(elem.Val == 1)
            acc.Last().Add(elem);
        return acc;
    }, acc => acc.Where(x => x.Count != 0));

int groupNum = 1;
foreach (var group in grouped)
{
    Console.WriteLine($"Group {groupNum++}");
    foreach (var item in group)
        Console.WriteLine($"{item.Id} - {item.Val}");
}

/* output:
Group 1
3 - 1
4 - 1
5 - 1
6 - 1
Group 2
11 - 1
12 - 1
*/

这假定可以在出现0值之前添加具有1值的条目,而且我不确定这比使用foreach更易读,并且实现一个扩展方法可能会更好无论如何都可能会使用foreach。

答案 2 :(得分:0)

尝试this solution;

var prev = 0;
List<int> bag = null;
var result = new List<List<int>>();

foreach(var item in list)
{
    if(item.Val == 1)
    {
        if(prev == 0)
        {
            if(bag != null)
                result.Add(bag);
            bag = new List<int>();
        }                       
        bag.Add(item.Id);
    }
    prev = item.Val;
}
if(bag != null)
    result.Add(bag);

result.ForEach(x => {           
    Console.WriteLine(String.Join(", ", x));
});
//3, 4, 5, 6
//11, 12