4

私はこれを探し回っていますが、Linqがあまり得意ではないため、これを見逃していると確信しています。

次のようなリストがあります。

type=a, value=aaaa
type=a, value=bbbb
type=b, value=cccc
type=d, value=dddd
type=d, value=eeee
type=d, value=ffff
type=a, value=gggg
type=b, value=hhhh
type=b, value=iiii
type=b, value=jjjj

これをソートせずにサブリストに分割したいと思います(元の順序を維持する必要があります)。これらのリストを、リストのリストまたは同様のものでこの順序で取得したいと思います。

List 1
type=a, value=aaaa
type=a, value=bbbb

List2
type=b, value=cccc

List 3
type=d, value=dddd
type=d, value=eeee
type=d, value=ffff

List 4
type=a, value=gggg

List 5
type=b, value=hhhh
type=b, value=iiii
type=b, value=jjjj

ループは最善の答えではないと思います。

どんなアイデアでも大歓迎です。

ロングライブstackoverflow.com!

クリス

4つの回答の後に編集:

私は以下からの答えをチェックしました:*謎性*バートエバンス*リスキーマーティン* david.s

それらはすべてうまく機能します。Bert Evansがパフォーマンスを向上させました。この場合、これは私にとって大きな問題ではありませんが、投稿のために簡単なチェックを行いました。

私は誰のコードも変更しませんでした。かなり短いリストでこれらの操作の4,000を実行するタイミングをとっただけです。

リスキーの答えは最速でした。バートの答えは少し遅かった。

davidとEnigmativityは、実際にはほとんど遅くなりませんでした。

私は、パフォーマンスと関連する投稿を早い段階で指摘し、その後戻ってきて回答を提供したことから、Riskyの回答を承認済みとしてマークしました。

しかし、私はバートのものが最も読みやすいことに同意します。

正直なところ、どちらを使用するかわかりません...実際には、サブグループごとに値と1つのキーのみが必要であることをすでに考慮しているため、Enigmativityのソリューションを使用します。

4

4 に答える 4

4

この回答とこの回答からわずかに変更された拡張メソッドを使用する:

public static IEnumerable<IGrouping<int, T>> GroupConsecutive<T>(this IEnumerable<T> set, Func<T, T, bool> predicate)
{
    var i = 0;
    var k = 0;
    var ranges = from e in set
                    let idx = ++i
                    let next = set.ElementAtOrDefault(idx)
                    let key = next == null ? k : predicate(e, next) ? k : k++
                    group e by key into g
                    select g;
    return ranges;
}

クラスが与えられた場合:

public class Foo
{
    public string Type { get; set; }
    public string Value { get; set; }
}

あなたはこれを行うことができます:

List<Foo> list = new List<Foo>()
{
    new Foo() { Type = "a", Value = "aaaa" },
    new Foo() { Type = "a", Value = "bbbb" },
    new Foo() { Type = "b", Value = "cccc" },
    new Foo() { Type = "d", Value = "dddd" },
    new Foo() { Type = "d", Value = "eeee" },
    new Foo() { Type = "d", Value = "ffff" },
    new Foo() { Type = "a", Value = "gggg" },
    new Foo() { Type = "b", Value = "hhhh" },
    new Foo() { Type = "b", Value = "iiii" },
    new Foo() { Type = "b", Value = "jjjj" }
};

var groups = list.GroupConsecutive((a, b) => a.Type == b.Type);

foreach (var group in groups)
{
    Console.WriteLine("List " + group.Key);
    foreach (var item in group)
    {
        Console.WriteLine("Type=" + item.Type + "    Value=" + item.Value);
    }
    Console.WriteLine();
}

結果は次のようになります。

List 0
Type=a    Value=aaaa
Type=a    Value=bbbb

List 1
Type=b    Value=cccc

List 2
Type=d    Value=dddd
Type=d    Value=eeee
Type=d    Value=ffff

List 3
Type=a    Value=gggg

List 4
Type=b    Value=hhhh
Type=b    Value=iiii
Type=b    Value=jjjj
于 2012-07-21T23:16:20.910 に答える
3

これは一般化するか、拡張メソッドにすることができますが、アイデアは次のとおりです。

public static IEnumerable<List<Item>> GroupConsecutive(IEnumerable<Item> items)
{
    if (items.Any())
    {
        string firstType = items.Select(i => i.Type).First();
        var adjacents = items.TakeWhile(i => i.Type == firstType).ToList();
        yield return adjacents;
        foreach (var group in GroupConsecutive(items.Skip(adjacents.Count)))
        {
            yield return group;
        }
    }
}

このクラスの使用:

public class Item
{
    public string Type { get; set; }
    public string Value { get; set; }
}

編集:このソリューションのトレードオフは次のとおりです。

長所:

  • 遅延評価されたコレクションを返します
  • 変数を変更しません
  • 簡潔です

短所:

  • items2 回繰り返します。itemsが List の場合は大したことではありませんが、 がitemsアイテムごとに高価な計算を実行する IEnumerable の場合、このメソッドは他のメソッドよりも遅くなる可能性があります。

items遅延評価で 1 回繰り返したい場合は、この回答に記載されているようにGroupAdjacent拡張メソッドを使用するか、 でループすることをお勧めします。遅延評価なしで 1 回の繰り返しが必要な場合は、ループまたはメソッドをお勧めします。yield returnAggregate

于 2012-07-22T00:00:28.790 に答える
2

を使用した LINQ のみの回答を次に示しAggregateます。

私はこれから始めました:

var list = new []
{
    new { type="a", value="aaaa", },
    new { type="a", value="bbbb", },
    new { type="b", value="cccc", },
    new { type="d", value="dddd", },
    new { type="d", value="eeee", },
    new { type="d", value="ffff", },
    new { type="a", value="gggg", },
    new { type="b", value="hhhh", },
    new { type="b", value="iiii", },
    new { type="b", value="jjjj", },
};

そして、これを行いました:

var accumulator = new List<KeyValuePair<string, List<string>>>()
{
    new KeyValuePair<string, List<string>>(
        list.First().type,
        new List<string>()),
};

var results = list.Aggregate(accumulator, (a, x) =>
{
    if (a.Last().Key == x.type)
    {
        a[a.Count - 1].Value.Add(x.value);
    }
    else
    {
        a.Add(new KeyValuePair<string, List<string>>(
            x.type,
            new List<string>(new [] { x.value, })));
    }
    return a;
});

その後、次のresultsようになります。

結果

これがうまくいくかどうか教えてください。

于 2012-07-21T23:57:35.917 に答える
2

LINQ はなく、昔ながらの単純な C# であり、すべてのジュニア開発者がうまくいけば従うことができる単一のループです。言うまでもなく速い。

public class Foo
{
    public string Type { get; set; }
    public string Value { get; set; }
}

List<Foo> list = new List<Foo>()
{
    new Foo() { Type = "a", Value = "aaaa" },
    new Foo() { Type = "a", Value = "bbbb" },
    new Foo() { Type = "b", Value = "cccc" },
    new Foo() { Type = "d", Value = "dddd" },
    new Foo() { Type = "d", Value = "eeee" },
    new Foo() { Type = "d", Value = "ffff" },
    new Foo() { Type = "a", Value = "gggg" },
    new Foo() { Type = "b", Value = "hhhh" },
    new Foo() { Type = "b", Value = "iiii" },
    new Foo() { Type = "b", Value = "jjjj" }
};

Foo previous = null;
List<List<Foo>> separateLists = new List<List<Foo>>();
List<Foo> currentList = null;
foreach (var foo in list)
{
    if (null == previous || previous.Type != foo.Type)
    {
        currentList = new List<Foo>();
        separateLists.Add(currentList);
    }
    currentList.Add(foo);
    previous = foo;
}
于 2012-07-22T03:54:20.443 に答える