简体   繁体   English

使用LINQ从C#通用词典查询值

[英]Query values from a C# Generic Dictionary using LINQ

Here's the code i have: 这是我的代码:

Dictionary<double, long> dictionary = new Dictionary<double, long>();
dictionary.Add(99, 500);
dictionary.Add(98, 500);
dictionary.Add(101, 8000);
dictionary.Add(103, 6000);
dictionary.Add(104, 5);
dictionary.Add(105, 2000);

double price = 100;

the query i want is: the key that is nearest price AND with the lowest value. 我想要的查询是:最接近价格且键值最低的键。 so in the above example it should return 99. how do i code this in LINQ ? 因此,在上面的示例中,它应该返回99。如何在LINQ中对此进行编码? i have seen alot of linq examples but i cannt adapt any of them to my needs b/c my query has 2 conditions. 我已经看到了很多linq示例,但是我无法根据我的需要调整它们中的任何一个,因为我的查询有2个条件。

thanks for any help. 谢谢你的帮助。

edit: based on comments from @nintendojunkie and @DmitryMartovoi i have had to rethink my approach. 编辑:基于@nintendojunkie和@DmitryMartovoi的评论,我不得不重新考虑我的方法。 if i prioritize key closest to price then resulting value may not be the lowest and if i prioritize value first then the key may be too far from price so the query will have to prioritize BOTH the key and value the same and give me the lowest value with the closest key to price. 如果我对最接近价格的键进行优先级排序,则结果值可能不是最低的;如果我对优先级进行优先排序,则键值可能与价格相距太远,因此查询将必须对键值和优先级都进行优先排序,并给我最低的值价格最接近的关键。 both key and value are equally important. 关键和价值同等重要。 can anyone help on this? 有人可以帮忙吗? thanks 谢谢

您可以这样操作:

var result = dictionary.Select(c => new { c.Key, Diff = Math.Abs(price - c.Key) + Math.Abs(price - c.Value), c.Value }).OrderBy(c => c.Diff).FirstOrDefault();

Don't forget - you use dictionary. 别忘了-您使用字典。 Dictionary has only unique keys. 词典只有唯一的键。 I think you consider this structure as List<KeyValuePair<double, long>> . 我认为您认为此结构为List<KeyValuePair<double, long>> If so - please look to this example: 如果是这样,请查看以下示例:

var minimumKeyDifference = dictionary.Min(y => Math.Abs(y.Key - price));
var minimumItems = dictionary.Where(x => Math.Abs(x.Key - price).Equals(minimumKeyDifference));
var desiredKey = dictionary.First(x => x.Value.Equals(minimumItems.Where(y =>  y.Key.Equals(x.Key)).Min(y => y.Value))).Key;

You say that you need to find the closest price and the lowest value, but you don't define the rules for attributing precedence between two. 您说需要找到最接近的价格最低的价格,但是您没有定义两个属性之间的优先级分配规则。 In the below, I'm attributing them equal precedence: a price distance of 1 is equivalent to a value of 1. 在下面,我将它们的优先级赋予相同:价格距离1等于值1。

var closest = 
    dictionary.OrderBy(kvp => Math.Abs(kvp.Key - price) + kvp.Value).First();

The OrderBy(…).First() should be replaced by a MinBy(…) operator, if available, for performance. 为了提高性能,应该用MinBy(…)运算符替换OrderBy(…).First()

Edit : If the value is only meant to serve as a tiebreaker, then use this (also posted by Giorgi Nakeuri ): 编辑 :如果该值仅旨在充当决胜局,则使用此值(也由Giorgi Nakeuri发布):

var closest = 
    dictionary.OrderBy(kvp => Math.Abs(kvp.Key - price))
              .ThenBy(kvp => kvp.Value)
              .First();
var price = 100.0;

var nearestKey = (from pair in dictionary
                 let diff = Math.Abs(pair.Key - price)
                 select new {Key = pair.Key, Diff = diff}
                 order by diff desc).First().Key;
var minValue = dictionary[nearestKey];

Maybe you want a magic linq query but i suggest to try the in below. 也许您想要一个神奇的linq查询,但我建议尝试以下方法。

public static class MyExtensions
{
    public static double? GetNearestValue (this IDictionary<double, long> dictionary, double value)
    {
        if (dictionary == null || dictionary.Count == 0)
            return null;

        double? nearestDiffValue = null;
        double? nearestValue = null;

        foreach (var item in dictionary) {
            double currentDiff = Math.Abs (item.Key - value);
            if (nearestDiffValue == null || currentDiff < nearestDiffValue.Value) {
                nearestDiffValue = currentDiff;
                nearestValue = item.Value;
            }
        }

        return nearestValue;
    }
}

And call like this 像这样打电话

Console.WriteLine (dictionary.GetNearestValue (100d));
var min = dictionary
            .OrderBy(pair => pair.Value)
            .Select(pair =>
                new
                {
                    k = pair.Key,
                    d = Math.Abs(pair.Key - price)
                })
            .OrderBy(t => t.d)
            .Select(t => t.k)
            .FirstOrDefault();

The following works if you change your dictionary key's data type to decimal instead of double . 如果将字典键的数据类型更改为decimal而不是double

decimal price = 100;
decimal smallestDiff = dictionary.Keys.Min(n => Math.Abs(n - price));
var nearest = dictionary.Where(n => Math.Abs(n.Key - price) == smallestDiff)
                        .OrderBy(n => n.Value).First();

If you use double this may fail due to rounding issues, but decimal is preferred for anything having to do with money to avoid those issues. 如果使用double则可能会由于舍入问题而失败,但是对于与金钱有关的任何事情,为了避免这些问题,首选decimal

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM