9

オブジェクトのコレクションを持っています。概略的に:

[
    { A = 1, B = 1 }
    { A = 1, B = 2 }
    { A = 2, B = 3 }
    { A = 2, B = 4 }
    { A = 1, B = 5 }
    { A = 3, B = 6 }
]

必要:

[
    { A = 1, Bs = [ 1, 2 ] }
    { A = 2, Bs = [ 3, 4 ] }
    { A = 1, Bs = [ 5 ] }
    { A = 3, Bs = [ 6 ] }
]

そのようなLINQは可能ですか?

注:順序は重要です。だからBs = [5]融合できないBs = [1, 2]

4

7 に答える 7

2

GroupAdjacent拡張メソッドを使用できます。

次に、あなたはただ必要です

var grps = objects.GroupAdjacent(p => new { p.A });

それを実装する最も簡単な方法だと思います。

編集:

これが私のテストコードです。

class Program
{
    static void Main(string[] args)
    {
       var ia = new Dummycls[] { 
           new Dummycls{ A = 1, B = 1 },
           new Dummycls{ A = 1, B = 2 },
           new Dummycls{ A = 2, B = 3 },
           new Dummycls{ A = 2, B = 4 },
           new Dummycls{ A = 1, B = 5 },
           new Dummycls{ A = 3, B = 6 },

       };
        var groups = ia.GroupAdjacent(i => i.A);
        foreach (var g in groups)
        {
            Console.WriteLine("Group {0}", g.Key);
            foreach (var i in g)
                Console.WriteLine(i.ToString());
            Console.WriteLine();
        }

        Console.ReadKey();
    }
}

class Dummycls
{
    public int A { get; set; }
    public int B { get; set; }

    public override string ToString()
    {
        return string.Format("A={0};B={1}" , A , B);
    }
}

結果は

Group 1
A=1;B=1
A=1;B=2

Group 2
A=2;B=3
A=2;B=4

Group 1
A=1;B=5

Group 3
A=3;B=6
于 2012-07-17T00:00:14.623 に答える
2

これらの単純化されたクラスを考えると:

class C {
  public int A;
  public int B;
}
class R {
  public int A;
  public List<int> Bs = new List<int>();
}

次のように実行できます。

var cs = new C[] {
  new C() { A = 1, B = 1 },
  new C() { A = 1, B = 2 },
  new C() { A = 2, B = 3 },
  new C() { A = 2, B = 4 },
  new C() { A = 1, B = 5 },
  new C() { A = 3, B = 6 }
};

var rs = cs.
  OrderBy(o => o.B).
  ThenBy(o => o.A).
  Aggregate(new List<R>(), (l, o) => {
    if (l.Count > 0 && l.Last().A == o.A) {
      l.Last().Bs.Add(o.B);
    }
    else {
      l.Add(new R { A = o.A, Bs = { o.B } });
    }
    return l;
  });

: 上記では、B を並べ替えてから As を並べ替える必要があると想定しています。そうでない場合は、並べ替え命令を削除するだけです。

var rs = cs.
  Aggregate(new List<R>(), (l, o) => {
    if (l.Count > 0 && l.Last().A == o.A) {
      l.Last().Bs.Add(o.B);
    }
    else {
      l.Add(new R { A = o.A, Bs = { o.B } });
    }
    return l;
  });
于 2012-07-16T22:22:08.880 に答える
2

したがって、基本的には、同じA値を持ち、連続しているものをグループ化する必要があります。

オブジェクトのリストを、前/次の要素を含む匿名型に変換する必要があります。Selectsより赤くするために2つ使用しました。次に、2 つの要素が連続しているかどうか (隣接するインデックス) を確認する必要があります。これで、必要なGroupBy値とbool.

あなたのオブジェクト:

var list = new System.Collections.Generic.List<Foo>(){
    new Foo(){ A = 1, B = 1 },
    new Foo(){ A = 1, B = 2 },
    new Foo(){ A = 2, B = 3 },
    new Foo(){ A = 2, B = 4 },
    new Foo(){ A = 1, B = 5 },
    new Foo(){ A = 3, B = 6 }
};

クエリ:

var groups = list
    .Select((f, i) => new
    {
        Obj = f,
        Next = list.ElementAtOrDefault(i + 1),
        Prev = list.ElementAtOrDefault(i - 1)
    })
    .Select(x => new
    {
        A = x.Obj.A,
        x.Obj,
        Consecutive = (x.Next != null && x.Next.A == x.Obj.A)
                   || (x.Prev != null && x.Prev.A == x.Obj.A)
    })
    .GroupBy(x => new { x.Consecutive, x.A });

結果を出力します。

foreach (var abGroup in groups)
{
    int aKey = abGroup.Key.A;
    var bList = string.Join(",", abGroup.Select(x => x.Obj.B));
    Console.WriteLine("A = {0}, Bs = [ {1} ] ", aKey, bList);
}

これが実際のデモです: http://ideone.com/fXgQ3

于 2012-07-16T22:57:08.593 に答える
1

コレクションが にoある場合:

    var trans = o.Aggregate
    (
            new {
                List = new List<Tuple<int, List<int>>>(),
                            LastSeed = (int?)0
            },
            (acc, item) =>
            {
                if (acc.LastSeed == null || item.A != acc.LastSeed)
                    acc.List.Add(Tuple.Create(item.A, new List<int>()));
                acc.List[acc.List.Count - 1].Item2.Add(item.B);
                return new { List = acc.List, LastSeed = (int?)item.A};
            },
            acc => acc.List.Select(
                  z=>new {A = z.Item1,
                          B = z.Item2 as IEnumerable<int>
                         })
       );

これによりIEnumerable<int, IEnumerable<int>>、必要な形式の が生成されます。

于 2012-07-16T23:52:42.043 に答える
1

これは、あなたが望むことをするメソッドの構造です:

public static IEnumerable<IGrouping<TKey, TElement>> GroupWithKeyBreaks<T, TKey, TElement>(IEnumerable<T> enumerable,
        Func<T, TKey> keySelector,
        Func<T, TElement> itemSelector)    
{
    // Error handling goes here
    TKey currentKey = default(TKey);
    List<TElement> elements = new List<TElement>();

    foreach (T element in enumerable)
    {
        TKey thisKey = keySelector(element);
        if (thisKey == null)
        {
            continue;
        }

        if (!thisKey.Equals(currentKey) && elements.Count > 0)
        {
            yield return new SimpleGrouping<TKey, TElement>(currentKey, elements);
            elements = new List<TElement>();
        }

        elements.Add(itemSelector(element));
        currentKey = thisKey;
     }

    // Add the "last" item
    if (elements.Count > 0)
    {
        yield return new SimpleGrouping<TKey, TElement>(currentKey, elements);
    }
}

次のヘルパー クラスを使用します。

private class SimpleGrouping<T, U> : IGrouping<T, U>
{
    private T key;
    private IEnumerable<U> grouping;

    T IGrouping<T, U>.Key
    {
        get { return key; }
    }

    IEnumerator<U> IEnumerable<U>.GetEnumerator()
    {
        return grouping.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return grouping.GetEnumerator();
    }

    public SimpleGrouping(T k, IEnumerable<U> g)
    {
        this.key = k;
        this.grouping = g;
    }
}

使用例は次のとおりです。

foreach (var grouping in data.GroupWithKeyBreaks(x => x.A, x => x.B))
{
    Console.WriteLine("Key: " + grouping.Key);
    foreach (var element in grouping)
    {
        Console.Write(element);
    }
}
于 2012-07-16T22:01:08.267 に答える
1
var result = list.ToKeyValuePairs(x => x.A)
                 .Select(x => new { A = x.Key, Bs = x.Value.Select(y => y.B) }); 

foreach (var item in result)
{
    Console.WriteLine("A = {0} Bs=[{1}]",item.A, String.Join(",",item.Bs));
}

-

public static class MyExtensions
{
    public static IEnumerable<KeyValuePair<S,IEnumerable<T>>> ToKeyValuePairs<T,S>(
            this IEnumerable<T> list, 
            Func<T,S> keySelector)
    {
        List<T> retList = new List<T>();
        S prev = keySelector(list.FirstOrDefault());
        foreach (T item in list)
        {
            if (keySelector(item).Equals(prev))
                retList.Add(item);
            else
            {
                yield return new KeyValuePair<S, IEnumerable<T>>(prev, retList);
                prev = keySelector(item);
                retList = new List<T>();
                retList.Add(item);
            }
        }
        if(retList.Count>0)
            yield return new KeyValuePair<S, IEnumerable<T>>(prev, retList);
    }
}

出力:

A = 1 Bs=[1,2]
A = 2 Bs=[3,4]
A = 1 Bs=[5]
A = 3 Bs=[6]
于 2012-07-16T22:48:48.823 に答える
1
var groupCounter = 0;
int? prevA = null;

collection
    .Select(item => { 
        var groupId = item.A == prevA ? groupCounter : ++groupCounter; 
        prevA = item.A;
        return new { groupId, item.A, item.B }; 
    })
    .GroupBy(item => item.groupId)
    .Select(grp => new { A = grp.First().A, Bs = grp.Select(g => g.B) });
于 2012-07-16T23:08:17.850 に答える