7

LINQ を使用してデータをグループ化する方法と、それを個別の項目に分割する方法は知っていますが、部分的にのみグループ化を解除する方法がわかりません。

次のような一連のデータがあります。

var data = new Dictionary<Header, Detail>()
{
    { new Header(), new Detail { Parts = new List<string> { "Part1", "Part1", "Part2" } } }
};

これを正しく処理するには、重複部分のすべてのインスタンスを辞書の個別のエントリにする必要があります (ただし、辞書のままかどうかは問題ではありません -IEnumerable<KeyValuePair<Header, Detail>>完全に受け入れられます)。Partsただし、リストを完全に分割したくはありません。リストに別の部分を含めることは問題ありません。

具体的には、最終データを次のようにしたいと考えています。

{
  { new Header(), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
  { new Header(), new Detail { Parts = new List<string> { "Part1" } } },
}

より複雑な例:

var data = new Dictionary<Header, Detail>()
{
    { new Header(1), new Detail { Parts = new List<string> { "Part1", "Part1", "Part2" } } },

    { new Header(2), new Detail { Parts = new List<string> { "Part1", "Part2" } } },

    { new Header(3), new Detail { Parts = new List<string> { "Part1", "Part2", "Part2", "Part2", "Part3", "Part3"} } }
};

var desiredOutput = new List<KeyValuePair<Header, Detail>>()
{
    { new Header(1), new Detail { Parts = new List<string> { "Part1", "Part2" } } },
    { new Header(1), new Detail { Parts = new List<string> { "Part1" } } },

    { new Header(2), new Detail { Parts = new List<string> { "Part1", "Part2" } } },

    { new Header(3), new Detail { Parts = new List<string> { "Part1", "Part2", "Part 3" } } },
    { new Header(3), new Detail { Parts = new List<string> { "Part2", "Part3" } } },
    { new Header(3), new Detail { Parts = new List<string> { "Part2" } } }
};

何かアドバイス?

4

4 に答える 4

2

ここでは Linq はあまり役に立ちませんが、次の拡張メソッドを使用すると、このトリックを実行できます。

public static IEnumerable<KeyValuePair<Header, Detail>> UngroupParts(
    this IEnumerable<KeyValuePair<Header, Detail>> data)
{
    foreach (var kvp in data)
    {
        Header header = kvp.Key;
        List<string> parts = kvp.Value.Parts.ToList();
        do
        {
            List<string> distinctParts = parts.Distinct().ToList();
            Detail detail = new Detail() { Parts = distinctParts };
            yield return new KeyValuePair<Header, Detail>(header, detail);

            foreach (var part in distinctParts)
                parts.Remove(part);
        }
        while (parts.Any());
    }
}

使用法:

var desiredOutput = data.UngroupParts();
于 2012-11-07T15:29:09.093 に答える
2

いいえ、実際には、これらすべてを実行する既存の LINQ 関数はありません。

基本的に、Parts文字列ごとのグループ化を想像し、各グループを行と考えると、必要なのは各「列」です。これは、ヘルパー関数GetNthValues(LINQ スタイルの関数をモデル化するように設計されています) を使用して行いました。それができたら、各パーツをグループ化し、関数を呼び出し、結果を辞書に戻すだけです。

public static Dictionary<Header, Detail> Ungroup(Dictionary<Header, Detail> input)
{
    var output = new Dictionary<Header, Detail>();

    foreach (var key in input.Keys)
    {
        var lookup = input[key].Parts.ToLookup(part => part);

        bool done = false;

        for (int i = 0; !done; i++)
        {
            var parts = lookup.GetNthValues(i).ToList();
            if (parts.Any())
            {
                output.Add(new Header(key.Value), new Detail { Parts = parts });
            }
            else
            {
                done = true;
            }
        }
    }

    return output;
}

public static IEnumerable<TElement> GetNthValues<TKey, TElement>(
    this ILookup<TKey, TElement> source, int n)
{
    foreach (var group in source)
    {
        if (group.Count() > n)
        {
            yield return group.ElementAt(n);
        }
    }
}
于 2012-11-07T15:10:52.733 に答える
1

SortedSetパーツ内の要素から を作成しますDetail。これは に変換され、List最初のグループでありSortedSet、実際には の各要素に対して 1 つのインスタンスのみが含まれますDetail

Detail元のパーツ (またはそのコピー)から削除します。詳細のサイズがゼロになるまで繰り返します。

編集:

単一の Linq ステートメントに似たものを試しています。簡単にするためにリストを使用します

var total = new List<List<string>>() { 
    new List<string>(), 
    new List<string>(), 
    new List<string>(), 
    new List<string>(), 
    new List<string>(), 
    new List<string>() 
};

//the statement

var q = k.Aggregate(total, (listOlists, singleStrin) => {
    listOlists.Where(l => !l.Contains(singleStrin)).First().Add(singleStrin);
    return listOlists;
});

基本的に、リストに要素がまだ含まれていない場合にのみ、文字列のリストに要素を追加するアキュムレータ関数を作成します。リスト自体は、アキュムレータ リストに含まれています。アキュムレータ リストを初期化する必要があります。そうしないと、Linq ステートメントがさらに醜くなります。

于 2012-11-07T14:47:00.303 に答える
0

これにより、文字列のリストが重複のない複数の文字列リストに分割されます。

List<string> oldParts = new List<string> { "Part1", "Part2", "Part2", "Part2", "Part3", "Part3" };
List<List<string>> allLists = new List<List<string>>();

foreach (string currentPart in oldParts)
{
    foreach (List<string> currentList in allLists)
    {
         // if currentList doesn't have the part, then 
         //    add part to the currentList, and process next part
         if (!currentList.Contains(currentPart))
         {
             currentList.Add(currentPart);
             goto NextPart;
         }
    }
    // if we get here, the part is already contained on in the lists
    // so add a new list to allLists
    // and add the part to the new list
    allLists.Add(new List<string> { currentPart });

    NextPart: ;
}     
于 2012-11-07T14:57:46.960 に答える