使用十进制数字键在字典中查找最近的键

时间:2015-03-13 17:53:38

标签: swift dictionary key decimal closest

我有一些使用datePicker的快速代码,一旦你点击一个按钮就会告诉你当前日期和所选日期之间的年数和月份的差异使用小数。

所以例如:今天是2015年3月(0.3),以及我在2014年12月选择的选择器。当我点击按钮时它正确显示我0.4或今天和那个日期之间的差异。

现在,我的字典中有400多个键,所有十进制数字都是:

var dict: [CGFloat: String] = [5.6: "blabla", 32.4: "bla", etc.]

我怎么能告诉它检查一旦我点击按钮我得到的号码,在我的情况下var completeNumber,(比如说6.7)是字典键之一,如果它不是去了下一个最接近的号码/密钥?

2 个答案:

答案 0 :(得分:1)

字典不会开箱即用,它只会给你精确的关键匹配。

但是,由于它是一个集合,如果给定一个距离函数,你可以编写一个搜索最接近匹配的函数。这是一个通用的功能:

(注意我没有花太多时间调试这个,请谨慎使用!)

/// Search any collection for the index of the closest match, given a 
/// function to compute the distance between the elements and the target.
///
/// Returns nil in case of an empty collection.
func findClosestMatchTo<Col: CollectionType, Target, Distance: Comparable>
  (target: Target, within col: Col, #byDistance: (Target,Col.Generator.Element)->Distance) -> Col.Index? 
{
    var match: Col.Index? = nil
    var bestDistanceSoFar: Distance? = nil

    for (idx,elem) in zip(indices(col),col) {
        let thisDistance = byDistance(target, elem)
        if bestDistanceSoFar == nil || thisDistance < bestDistanceSoFar {
            match = idx
            bestDistanceSoFar = thisDistance
        }
    }

    return match
}

/// A convenience version for Strideable types, that can always have their distance computed
func findClosestMatchTo
  <Col: CollectionType where Col.Generator.Element: Strideable>
  (target: Col.Generator.Element, within col: Col) -> Col.Index? 
{
    return findClosestMatchTo(target, within: col) { abs($0 - $1) }
}

// prints 1 (index of 20)
println(findClosestMatchTo(100, within: [1,20,700,9]))
// prints 2 (index of 700)
println(findClosestMatchTo(1000, within: [1,20,700,9]))

现在,您只需编写距离函数,以便比较键/值对的关键部分:

let dict: [CGFloat: String] = [1: "waffle", 5.6: "blabla", 32.4: "bla"]
let closest = findClosestMatchTo(5.5, within: dict) { abs($0 - $1.0) }
if let idx = closest {
    dict[idx]  // prints (5.6, “blabla”)
}

当然,这个算法相对于字典的大小是O(n),所以如果你发现自己不断搜索匹配但很少插入或按键查找,你可能想看一个不同的数据结构或算法(可能是树或排序数组+二进制搜索)。

答案 1 :(得分:0)

以下是我写的一些可怕的代码,可以帮助您入门:

let items = [1.2: "Cheese", 4.3: "Bacon", 1.5: "Eggs", 8.5: "Toast"]

func findItemCloseTo(value: Double, inItems items: [Double: String]) -> (Double, String)? {
    if items.count == 0 {
        return nil
    }

    var foundNumber: Double?
    var foundString: String?

    var smallestDistance: Double = Double.infinity
    for (itemKey, itemValue) in items {
        let distance = fabs(itemKey - value)
        if distance < smallestDistance {
            smallestDistance = distance
            foundNumber = itemKey
            foundString = itemValue
        }
    }

    return (foundNumber!, foundString!)
}

findItemCloseTo(1.51, inItems: items) // Return(1.5, Eggs)
findItemCloseTo(3.0, inItems: items)  // Returns (4.3, Bacon)
findItemCloseTo(7.0, inItems: items)  // Returns (8.5, Toast)

我认为如果您将数据存储在字典中而不是存储在结构数组中,代码会更简单:

struct Item {
    var decimalDate: Double
    var data: String
}

您还可以优化事物并对数组进行排序以使搜索更快捷。使用字典,您必须遍历所有项目,因为密钥不以任何可排序的可预测方式存储。