2009-02-13 42 views
5

İki veya daha fazla dizim var - biri kimlikli, bir veya daha çok dize değeri olan. Bunları bir hash tablosuna birleştirmek istiyorum, böylece ID ile değerleri arayabilirim.C# dizilerinde birleştirme/birleştirme #

aşağıdaki fonksiyon iş yapar, ama daha kısa ve daha tatlı sürümü (LINQ?) Güzel olurdu:

Dictionary<int, string[]> MergeArrays(IEnumerable<int> idCollection, 
             params IEnumerable<string>[] valueCollections) 
{ 
    var dict = new Dictionary<int, string[]>(); 

    var idL = idCollection.Count(); 
    while (idL-- > 0) 
    { 
     dict[idCollection.ElementAt(idL)] = new string[valueCollections.Length]; 

     var vL = valueCollections.Length; 
     while (vL-- > 0) 
      dict[idCollection.ElementAt(idL)][vL] = valueCollections[vL].ElementAt(idL); 
    } 

    return dict; 
} 

Herhangi bir fikir?

cevap

3

Şu anda çok etkisiz - ElementAt'a yapılan tüm çağrılar her seferinde (gerektiği kadar) olabilir. (Bu, dizinin uygulanmasına bağlıdır.)

Ancak, bu kodun ne yaptığını bile anladığımdan emin değilim (çünkü foreach döngülerinin kullanılması kesinlikle daha net hale gelecektir, çünkü geriye doğru yerine yinelemeli) .? Eğer bazı örnek girişi ve beklenen çıktıları verebilir

EDIT:?. Tamam, ben burada neler olduğunu görmek düşünüyorum; etkili valueCollections özetliyorsanız sana isteyeceksiniz şüpheli bir şey gibi:

static Dictionary<int, string[]> MergeArrays(
    IEnumerable<int> idCollection, 
    params IEnumerable<string>[] valueCollections) 
{ 
    var valueCollectionArrays = valueCollections.Select 
     (x => x.ToArray()).ToArray(); 
    var indexedIds = idCollection.Select((Id, Index) => new { Index, Id }); 

    return indexedIds.ToDictionary(x => Id, 
     x => valueCollectionArrays.Select(array => array[x.Index]).ToArray()); 
} 

Oldukça çirkin olsa da, idCollection ile başlayacağınız bir dizi yapabilirseniz, bu daha kolay olacaktır.

DÜZENLEME: Tamam, bunun yerine diziler kullanabilirsiniz varsayarak: Ben düzelttim

static Dictionary<int, string[]> MergeArrays(
    int[] idCollection, 
    params string[][] valueCollections) 
{ 
    var ret = new Dictionary<int, string[]>(); 
    for (int i=0; i < idCollection.Length; i++) 
    { 
     ret[idCollection[i]] = valueCollections.Select 
      (array => array[i]).ToArray(); 
    } 
    return ret; 
} 

(umarım) ilk sürümünde bir hata - Ben değerlerin biraz dizisi oldu ve aralarında karışık alıyorum hangi değildi. İkinci versiyon deklarasyon değil, ama bence kişisel olarak daha net.

+0

> tüm bu çağrılar IEnumerable uyguluyor bağlıdır bütün dizisi geçmekte edilecektir. Aslında bir dizi ise çok hızlı olacaktır. Bağlı bir liste gibi bir şey kullanırsanız, verimsiz olur. –

+0

@Dan: Enumerable.ElementAt'ın IList 'un bir uygulamasının denetlendiğinin farkında değildim. Bu büyük bir fark yapar. Düzenleyecek –

+0

Benim durumumda, hem idCollection hem de valueCollections dizilerdir. Sadece daha jenerik yapmaya çalıştım - gelecekteki ihtiyaçları öngörmek için dikkat çekmeye çalışan klasik bir örnek. Bunlar katılmam gereken form verisi dizileridir ve asla dizilerden başka bir şey olmayacaklardır. – Bergius

1

Bir şey özlemediğim sürece, dizileri çoğaltmak için koda ihtiyacınız yoktur - bunlar kendi başlarına nesnelerdir ve beklenmedik bir şekilde kaybolmazlar.

Değerleri anonim bir sınıf kullanarak birleştirmek için Select() kullanın, daha sonra bunları şişirmek için ToDictionary() kullanın.

bu deneyin:

IDictionary<int, IEnumerable<string>> MergeArrays2(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var values = valueCollections.ToList(); 
     return idCollection.Select(
      (id, index) => new { Key = id, Value = values[index] }) 
      .ToDictionary(x => x.Key, x => x.Value); 
    } 

Not dönüş tipi yerine [] string IEnumerable kullanır - IMHO sen diziler kullanmak yerine bu daha esnek bulacaksınız.

Ayrıca, dönüş türü bir Arabirim kullanıyor.

Güncelleme: John Skeet, dizilerin değiştirilebileceği iyi bir noktaya (aşağıya bakın) yaptı ve bu bir sorun olabilir.

IDictionary<int, IEnumerable<string>> MergeArrays2(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var values = valueCollections.ToList(); 
     return idCollection.Select(
      (id, index) => new 
      { 
       Key = id, 
       Value = values[index].ToArray() // Make new array with values 
      }) 
      .ToDictionary(x => x.Key, x => x.Value); 
    } 
+0

Dizileri yeniden "oluşturmak" için iki neden var - ilki aslında mevcut dizileri alma değil, bir dizi * her * dizi alarak bir dizi oluşturuyor. Ancak, olmasalar bile, daha sonra * mutasyon geçirebilirlerdi. Bir sorun olabilir ya da olmayabilir. –

2

ne dersiniz:

 public static Dictionary<int, string[]> MergeArrays2(IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var dict = new Dictionary<int, string[]>(); 
     var valEnums = (from v in valueCollections select v.GetEnumerator()).ToList(); 
     foreach (int id in idCollection) 
     { 
      var strings = new List<string>(); 
      foreach (var e in valEnums) 
       if (e.MoveNext()) 
        strings.Add(e.Current); 
      dict.Add(id, strings.ToArray()); 
     } 
     return dict; 
    } 

veya biraz skeet cevap (Bende işe yaramadı) düzenleyerek:

 static Dictionary<int, string[]> MergeArrays_Skeet(IEnumerable<int> idCollection,params IEnumerable<string>[] valueCollections) 
    { 
     var valueCollectionArrays = valueCollections.Select(x=>x.ToArray()).ToArray(); 
     var indexedIds = idCollection.Select((Id, Index) => new { Index, Id }); 
     return indexedIds.ToDictionary(x => x.Id,x => valueCollectionArrays.Select(array => array[x.Index]).ToArray()); 
    } 
1
taze diziler oluşturmanın kolay değişim için aşağıya bakın

İşte bazı zarafet. Sevdiğimden daha uzun, ama çok kullanışlıdır.

public Dictionary<int, string[]> MergeArrays(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections 
      ) 
    { 
     Dictionary<int, int> ids = idCollection 
      .ToDictionaryByIndex(); 
     // 
     Dictionary<int, List<string>> values = 
      valueCollections.Select(x => x.ToList()) 
      .ToList() 
      .Pivot() 
      .ToDictionaryByIndex(); 
     // 
     Dictionary<int, string[]> result = 
      ids.ToDictionary(
       z => z.Value, 
       z => values[z.Key].ToArray() 
      ); 

     return result; 
    } 

Ve işte kullandığım yardımcı yöntemler.

public static List<List<T>> Pivot<T> 
     (this List<List<T>> source) 
    { 
     return source 
      .SelectMany((it) => 
       it.Select((t, i) => new { t, i }) 
      ) 
      .GroupBy(z => z.i) 
      .Select(g => g.Select(z => z.t).ToList()) 
      .ToList(); 
    } 

    public static Dictionary<int, T> ToDictionaryByIndex<T> 
     (this IEnumerable<T> source) 
    { 
     return source 
      .Select((t, i) => new { t, i }) 
      .ToDictionary(z => z.i, z => z.t); 
    } 

Yasal Uyarı: Bir dikdörtgen olmayan yapısıyla Pivot ararsanız, G/biliyorum ne olacağını umursamıyorum.

0

İstediğiniz kadar genel değil, ancak iki numaraya çiftler halinde sayılabilir. Bu sonucu bir sözlük haline dönüştürebilmek oldukça kolay. ElementAt için

public static class ExtensionMethods 
{ 
    public static IEnumerable<Pair<TOuter, TInner>> InnerPair<TInner, TOuter>(this IEnumerable<TOuter> master, 
                     IEnumerable<TInner> minor) 
    { 
     if (master == null) 
      throw new ArgumentNullException("master"); 
     if (minor == null) 
      throw new ArgumentNullException("minor"); 
     return InnerPairIterator(master, minor); 
    } 

    public static IEnumerable<Pair<TOuter, TInner>> InnerPairIterator<TOuter, TInner>(IEnumerable<TOuter> master, 
                       IEnumerable<TInner> minor) 
    { 
     IEnumerator<TOuter> imaster = master.GetEnumerator(); 
     IEnumerator<TInner> iminor = minor.GetEnumerator(); 
     while (imaster.MoveNext() && iminor.MoveNext()) 
     { 
      yield return 
       new Pair<TOuter, TInner> { First = imaster.Current, Second = iminor.Current }; 
     } 
    } 
} 


public class Pair<TFirst, TSecond> 
{ 
    public TFirst First { get; set; } 
    public TSecond Second { get; set; } 
}