如何使用Linq(C#)分离所有连续对象

时间:2010-11-04 19:17:56

标签: c# linq

我有List<MyObject> ... MyObject有一个名为 LineId (整数)...

的属性

所以,我需要在列表中单独连接所有LineId ...

示例:

List<MyObject> :

LineId =1
LineId =1
LineId =1
LineId =2
LineId =1
LineId =2

结果必须是4个单独列表:

LineId =1
LineId =1
LineId =1
-------------
LineId =2
-------------
LineId =1
-------------
LineId =2

所以,我必须将所有连续的LineId分开......

使用Linq有一种简单的方法吗?

由于

6 个答案:

答案 0 :(得分:3)

嗯,我不认为linq是解决这个问题的最干净的解决方案。我认为以下代码可能比任何LINQ都简单易读。

            List<MyObject> currentList = new List<MyObject>();
            List<List<MyObject>> finalList = new List<List<MyObject>>();
            for (int i = 0; i < myObjects.Count; i++)
            {
                //If the list is empty, or has the same LineId, add to it.
                if (currentList.Count == 0 || currentList[0].LineId == myObjects[i].LineId)
                {
                    currentList.Add(myObjects[i]);
                }
                //Otherwise, create a new list.
                else
                {
                    finalList.Add(currentList);
                    currentList = new List<MyObject>();
                    currentList.Add(myObjects[i]);
                }
            }
            finalList.Add(currentList);

我知道你要求linq,但我认为这可能是一个更好的解决方案。

答案 1 :(得分:3)

为了更好的衡量,LINQ解决方案如何使用LINQ实现:

public static IEnumerable<IEnumerable<TSource>> GroupConsecutive<TSource,TKey>(
        this IEnumerable<TSource> source, Func<TSource,TKey> keySelector) {
    if (source == null) throw new ArgumentNullException("source");
    if (keySelector == null) throw new ArgumentNullException("keySelector");

    var comparer = EqualityComparer<TKey>.Default;
    var grouped = new List<TSource>();
    using (var iter = source.GetEnumerator()) {
        if (!iter.MoveNext()) yield break;
        grouped.Add(iter.Current);
        var last = iter.Current;
        while (iter.MoveNext()) {
            if (!comparer.Equals(keySelector(iter.Current), keySelector(last))) {
                yield return grouped.AsReadOnly();
                grouped = new List<TSource>();
            }
            grouped.Add(iter.Current);
            last = iter.Current;
        }
        yield return grouped.AsReadOnly();
    }
}

答案 2 :(得分:2)

这看起来很难看,但这个想法很明确,我觉得它很有效:

var agg = list.Aggregate(
    new List<List<Line>>(),
    (groupedLines, line) => {
        if (!groupedLines.Any()) {
            groupedLines.Add(new List<Line>() { line });
        }
        else {
            List<Line> last = groupedLines.Last();
            if (last.First().LineId == line.LineId) {
                last.Add(line);
            }
            else {
                List<Line> newGroup = new List<Line>();
                newGroup.Add(line);
                groupedLines.Add(newGroup);
            }
        }
        return groupedLines;
    }
);

我假设你有:

class Line { public int LineId { get; set; } }

List<Line> list = new List<Line>() {
    new Line { LineId = 1 },
    new Line { LineId = 1 },
    new Line { LineId = 1 },
    new Line { LineId = 2 },
    new Line { LineId = 1 },
    new Line { LineId = 2 }
};

现在如果我执行

foreach(var lineGroup in agg) {
    Console.WriteLine(
        "Found {0} consecutive lines with LineId = {1}",
        lineGroup.Count,
        lineGroup.First().LineId
    );
    foreach(var line in lineGroup) {
        Console.WriteLine("LineId = {0}", line.LineId);
    }
}

我明白了:

Found 3 consecutive lines with LineId = 1
LineId = 1
LineId = 1
LineId = 1
Found 1 consecutive lines with LineId = 2
LineId = 2
Found 1 consecutive lines with LineId = 1
LineId = 1
Found 1 consecutive lines with LineId = 2
LineId = 2

打印在控制台上。

答案 3 :(得分:2)

稍微小一点的解决方案,但可能(读:绝对)不太清楚:

var lst = new [] {
    new { LineID = 1 },
    new { LineID = 1 },
    new { LineID = 1 },
    new { LineID = 2 },
    new { LineID = 1 },
    new { LineID = 2 },
};

var q = lst
    .Select((x,i) => new {Item = x, Index = i})
    .GroupBy(x => x.Item)
    .SelectMany(x => x.Select((y,i) => new { Item = y.Item, Index = y.Index, Sort = i - y.Index }))
    .OrderBy(x => x.Index)
    .GroupBy(x => x.Sort)
    .Select(x => x.Select(y => y.Item));

返回IEnumerable<IEnumerable<int>>

1,1,1
2
1
2

注意,如果您不关心订单,可以删除匿名对象中的“OrderBy”行和“Index = y.Index”属性,它将返回:

1,1,1
1
2
2

答案 4 :(得分:1)

这是一个解决方案,其中行按每个连续子序列中最后一项的索引进行分组。

在您的示例中,这些是边界索引:

LineId    Index     Boundary Index
  1         0           2
  1         1           2
  1         2           2
  2         3           3
  1         4           4
  2         5           5

以下是Linq查询:

var lines = new List<MyObject>
                {
                    new MyObject { LineId = 1 },
                    new MyObject { LineId = 1 },
                    new MyObject { LineId = 1 },
                    new MyObject { LineId = 2 },
                    new MyObject { LineId = 1 },
                    new MyObject { LineId = 2 },
                };

IEnumerable<List<int>> consecutiveLineIds = lines
    .Select((line, i) => new
    {
        Line = line,
        Boundary = i + lines.Skip(i)
                            .TakeWhile(l => l.LineId == line.LineId).Count() - 1
    })
    .GroupBy(item => item.Boundary, item => item.Line.LineId)
    .Select(g => g.ToList());

答案 5 :(得分:0)

void Main()
{
    var list = new []{
        new { LineId = 1 },
        new { LineId = 1 },
        new { LineId = 1 },
        new { LineId = 2 },
        new { LineId = 2 },
        new { LineId = 1 },
        new { LineId = 1 },
        new { LineId = 3 },
        new { LineId = 3 },
        new { LineId = 1 }
    };

    var groups =    
        from i in list
        group i by i.LineId into g
        select new { LineId = g.Key, MyObject = g };
}