2014-05-13 17 views
5

DateTime'ın arama değeri olarak Date bölümüne sahibim ve Dictionary<DateTime, double> türünde bir Sözlük'te eşleşen değeri almak istiyorum. DateTime tuşları sadece Tarih kısmı olarak saklanır.Sözlük'te en yakın DateTime anahtarını bulun <DateTime, çift>

Sorunum, arama değerimle eşleşen bir anahtar olmayabilir. Daha sonra yapmak istediğim, en yakın geçmiş dateTime.Date anahtarını ve eşleşen değeri bulmaktır.

Şimdi, sözlüklerin anahtar ile sıralanmadığının farkındayım. Bir SortedDictionary kullanabilirdim, ancak belirli bir nedenden dolayı bir Sözlük kullanmayı tercih ediyor ya da bir Liste koleksiyonuna geçiyorum (önceden sıralanabilir). Sorum şu: Bu durumda ne yapmanız önerilir: Eşleşen bir anahtar bulana kadar Sözlük yapısını korumak ve arama değerinimi azaltmak daha verimli olur mu? Ya da bir liste koleksiyonu kullanmak ve Linq kullanmak daha iyi olur mu? Her Sözlük yaklaşık 5000 anahtar/değer çifti içerir. aramalarının sıklığı oldukça yüksek (potansiyel olarak birçok yüz bin kere (her arama daha önceki değerden farklı olması sağlanır) olduğu için Ayrıca, yüksek sayısal olarak verimli bir çözüm aramaya unutmayın optomisation prematurley dert niye

+0

_specific Ne reason_ bir 'SortedDictionary' kaçınmak gerekiyor? –

+1

5000 büyük bir sayı değil. Ne sıklıkla bulmanız gerekiyor? –

+0

@TimSchmelter, koleksiyonu seri hale getirilmiş json dizesi olarak saklıyorum ve sıralı sözlükleri serileştirme konusunda iyi bir deneyimim yok. Ama ben burada SortedDictionary kullanmak için en mantıklı ise, kesinlikle farklı bir yapıya dönüştürebilirim. –

cevap

4

Hızlı ihtiyacınız olduğu için, en iyi şeyin BinarySearch sonuçlarını kullanmak olacağını düşünüyorum. Bu, sıralanan bir List<T> gerektirir.

int result = myList.BinarySearch(targetDate); 
if (result >= 0) 
    return myList[result]; 
else 
{ 
    int nextLarger = ~result; 
    // return next smaller, or if that doesn't exist, the smallest one 
    return myList[Math.Max(0, nextLarger - 1)]; 
} 

Bir Dictionary<TKey,TValue> ve hala Dictionary<TKey,TValue> gibi serializes sıralama yapıldığını List<TKey> birleştiren bir sınıf oluşturmak mümkün olmalıdır. Serileştirme, sınıfınıza bir [JsonConverter(typeof(KeyValuePairConverter))] eklemek kadar basit (Json.NET'te) olabilir.

var result = myDict.Keys.Where(x => x < targetDate).Max(); 
+0

Harika teşekkürler, deneyeyim ve geri bildiriyorum. –

+0

linq sorgunuzu hızlı bir şekilde sorgular: OrderByDescending bu özel örnekte Where cümlesinden önce olmamalı mı? –

+0

Aynı sonucu her iki şekilde de alacaksınız. Yazdıklarımın daha hızlı olacağını umuyorum, çünkü çok fazla ürün sıralamayacaksınız. Sonra tekrar [yanlış olabilir] (http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array). –

0

yapmak o

, anca o sonra yavaş Eğer bir sorununuz varsa onun ise hangi noktada başka yollar ve pro deneyin point bir profil ile

sonra anlayarak başlar ölçün onları dosyala.

Cevap şudur: Eğer herhangi bir şekilde yaparsanız ve bir performans sorunu yoksa, sadece zamandan tasarruf edip, gününüze değer katmak için başka bir şey yapmak için başarılı olursunuz.

Erken en iyi duruma getirme, yalnızca nereye bakmanız gerektiği konusunda tamamen yanlış olacağınız anlamsız değildir.

2

Özel bir yapıya kullanmak ve olacaktır: hız çok önemli olmasaydı Sadece şeyiyle ve vaka diğerlerinde

, gelecekte bu okumak, bunları daha basit böyle bir şey ile bunu yapabilir toplama işlemi bu bilgileri saklamak için: Burada

public struct DateValue 
{ 
    public DateValue(DateTime date, double val) 
     : this() 
    { 
     this.Date = date; 
     this.Value = val; 
    } 
    public DateTime Date { get; set; } 
} 

tüm DateValues tutan ve en yakın dönüş mantığını kapsüller bir koleksiyon olası bir uygulamasıdır. Bulmak için List.BinarySearch kullanıyor.

Belirtilen dizideki belirtilen değerin endeksi değeri ise bulundu: doğrudan bir eşleşme bulamazsa o hangi yakın algılamak için BinarySearch mantığını kullanır. Değer bulunamazsa ve değer, dizideki bir veya daha fazla öğesinden daha azsa, değerden daha büyük olan ilk öğenin indeksinin bit değeri tamamlayıcısı olan negatif bir sayıdır. değeri bulunamazsa ve değer dizideki öğelerin herhangi birinden daha büyükse, bit değeri tamamlayıcısı olan negatif numarası ( son öğenin artı 1'i).

public class DateValueCollection : List<DateValue>, IComparer<DateValue> 
{ 
    public DateValueCollection() { } 

    public DateValueCollection(IEnumerable<DateValue> dateValues, bool isOrdered) 
    { 
     if (isOrdered) 
      base.AddRange(dateValues); 
     else 
      base.AddRange(dateValues.OrderBy(dv => dv.Date)); 
    } 

    public DateValue GetNearest(DateTime date) 
    { 
     if (base.Count == 0) 
      return default(DateValue); 

     DateValue dv = new DateValue(date, 0); 
     int index = base.BinarySearch(dv, this); 

     if (index >= 0) 
     { 
      return base[index]; 
     } 
     // If not found, List.BinarySearch returns the complement of the index 
     index = ~index; 

     DateValue[] all; 
     if(index >= base.Count - 1) 
     { 
      // proposed index is last, check previous and last 
      all = new[] { base[base.Count - 1], base[base.Count - 2] }; 
     } 
     else if(index == 0) 
     { 
      // proposed index is first, check first and second 
      all = new[] { base[index], base[index + 1] }; 
     } 
     else 
     { 
      // return nearest DateValue from previous and this 
      var thisDV = base[index]; 
      var prevDV = base[index - 1]; 
      all = new[]{ thisDV, prevDV }; 
     } 
     return all.OrderBy(x => (x.Date - date).Duration()).First(); 
    } 

    public int Compare(DateValue x, DateValue y) 
    { 
     return x.Date.CompareTo(y.Date); 
    } 
} 

Hızlı testi:

var dateVals = new[] { 
    new DateValue(DateTime.Today.AddDays(10), 1), new DateValue(DateTime.Today, 3), new DateValue(DateTime.Today.AddDays(4), 7) 
}; 
var dvCollection = new DateValueCollection(dateVals, false); 
DateValue nearest = dvCollection.GetNearest(DateTime.Today.AddDays(1)); 
+0

Bu oldukça basit görünüyor, sindirim ve geri rapor etmeme izin verin. –

+0

@MattWolf: İyileştirme için kesinlikle yer var (yani, sırasız bir koleksiyonun "enjekte edilmesi" mümkündür), ancak size bir fikir vermelidir. Statik karşılaştırmanın gereksiz olduğunu, kaldırdığımı unutmayın. Sınıf IComparer 'yi uyguladığı için' base.BinarySearch (dv, this) 'komutunu kullanabilirsiniz. –

+0

Önerilen çözümden gerçekten çok hoşlanıyorum ve çok hızlı olmasına rağmen, sonunda Tim S. barebones yaklaşımını tercih ettim. Yapabilseydim birkaç kez tekrar ederdim. –