【问题标题】:Find closest item in array that matches criteria in Swift?在 Swift 中查找匹配条件的数组中最近的项目?
【发布时间】:2016-03-17 19:55:23
【问题描述】:

当我引用数组中的某个项目时,我想找到最接近它的另一个符合特定条件(向前或向后)的项目。

例如,我有这个数组:

let items = [
    (a: "Item 1", b: "F", c: 3),
    (a: "Item 2", b: "S", c: 5),
    (a: "Item 3", b: "D", c: 7),
    (a: "Item 4", b: "A", c: 9),
    (a: "Item 5", b: "M", c: 11),
    (a: "Item 6", b: "I", c: 13),
    (a: "Item 7", b: "F", c: 15),
    (a: "Item 8", b: "S", c: 17),
    (a: "Item 9", b: "D", c: 19),
    (a: "Item 10", b: "A", c: 21),
    (a: "Item 11", b: "M", c: 23),
    (a: "Item 12", b: "I", c: 13),
    (a: "Item 13", b: "F", c: 15),
    (a: "Item 14", b: "S", c: 17),
    (a: "Item 15", b: "D", c: 19),
    (a: "Item 16", b: "A", c: 21),
    (a: "Item 17", b: "M", c: 23),
    (a: "Item 18", b: "I", c: 13),
    (a: "Item 19", b: "F", c: 15),
    (a: "Item 20", b: "S", c: 17),
    (a: "Item 21", b: "D", c: 19),
    (a: "Item 22", b: "A", c: 21),
    (a: "Item 23", b: "M", c: 23),
    (a: "Item 24", b: "I", c: 13)
]

现在假设我有item[7],我怎样才能找到最接近具有b = "I" 的项目?我只能想到一些嵌套的 for 循环,但听起来很乱而且性能不好。另外请记住,我不希望在搜索时出现out of range 问题。关于如何处理这个问题的任何类似 Swift 的想法?

【问题讨论】:

    标签: arrays swift swift2


    【解决方案1】:

    使用高阶函数的优化解决方案:

     func closestMatch(values: [Int64], inputValue: Int64) -> Int64? {
        return (values.reduce(values[0]) { abs($0-inputValue) < abs($1-inputValue) ? $0 : $1 })
    }
    

    Swift 在每个版本中都在进步,以优化性能和效率。对于高阶函数,使用此实现更容易在值数组中找到最接近的匹配项。根据需要更改值的类型。

    【讨论】:

      【解决方案2】:

      假码:

      int index = 7; 
      
      int delta = 0;
      while (true)
      {
          delta = delta + 1;
      
          if ( index - delta >= 0 && matches(items[ index - delta]) {
      
               // Found closest index on Left :  index-delta
      
               break;
          }
      
          if ( index + delta < items.length && matches(items[ index + delta]) {
      
               // Found closest index on Right:  index + delta
      
               break;
          }
      }
      
      // Time complexity: O(N)
      

      您可以轻松地将假代码转换为 Swift 代码。

      【讨论】:

        【解决方案3】:

        以下是 Array 上的泛型,它应该可以满足您的需求。它返回一个包含最接近匹配的索引和值的元组:

        extension Array {
            func closestMatch(index:Index, predicate:(Element)->Bool) -> (Int, Element)? {
        
                if predicate(self[index]) {
                    return (index, self[index])
                }
        
                var delta = 1
        
                while(true) {
                    guard index + delta < count || index - delta >= 0 else {
                        return nil
                    }
        
                    if index + delta < count && predicate(self[index + delta]) {
                        return (index + delta, self[index + delta])
                    }
        
                    if index - delta >= 0 && predicate(self[index - delta]) {
                        return (index - delta, self[index - delta])
                    }
        
                    delta = delta + 1
                }
            }
        }
        
        print(items.closestMatch(7) { $0.1 == "I" })
        

        【讨论】:

          【解决方案4】:

          除了其他解决方案,“单线”:

          let index = 7
          let searchString = "I"
          
          let result = items.enumerate()
              .filter { $0.1.b == searchString }
              .map { (abs(index - $0.0), $0.1) }
              .minElement { $0.0 < $1.0 }
              .map { $0.1 }
          
          print(result) // Optional(("Item 6", "I", 13))
          

          根据大卫·贝瑞的回答:

          extension Array {
              func closestMatch(index: Index, predicate: (Element) -> Bool) -> Element? {
                  return enumerate().filter { predicate($0.1) }.map { (abs(index - $0.0), $0.1) }.minElement { $0.0 < $1.0 }.map { $0.1 }
              }
          }
          
          print(items.closestMatch(7) { $0.1 == "I" }) // Optional(("Item 6", "I", 13))
          

          注意: 性能方面,David Berry 的答案更好。

          【讨论】:

            【解决方案5】:

            这个功能应该可以工作:

            func findClosestItem(index: Int, items: [(a: String, b: String, c: Int)]) -> (a: String, b: String, c: Int)? {
            
                guard index < items.count && index > -1 else{
                    return nil
                }
                var foundItem: (String, String, Int)? = nil
                var closestBefore = Int(INT16_MAX)
                for i in (index - 1).stride(to: 0, by: -1) {
                    if items[index].b == items[i].b {
                        closestBefore = index - i
                        foundItem = items[i]
                        break
                    }
                }
            
                var closestAfter = Int(INT16_MAX)
                for i in index + 1 ..< items.count {
                    if items[index].b == items[i].b {
                        closestAfter = i - index
                        if closestAfter < closestBefore {
                            foundItem = items[i]
                        }
                        break
                    }
                }
            
                return foundItem
            }
            

            如果没有其他匹配项或索引无效,则返回 nil,否则返回该项。

            它只是向后搜索直到找到匹配的项目,然后向前搜索直到找到匹配的项目,并返回最接近的项目。

            【讨论】:

              【解决方案6】:

              最简单的应该是:

              let startIndex = 7
              if startIndex < items.count,
                let nextIndex = items[startIndex+1..<items.endIndex].indexOf({ $0.b == items[startIndex].b }) {
                  print(items[nextIndex]) // => "("Item 14", "S", 17)\n"
              }
              

              这不是问题的完整答案,只是进行前向搜索的示例。我意识到它与我发布后的要求不符,但我会留下它作为部分示例。

              【讨论】:

              • 创意简洁,但只向前搜索。
              • 结果也不是 OP 想要的。
              • 发布后,我意识到我误解了 OP 的要求。我将把它作为一个简洁的 find next 示例,它可以用来制作一个完整的解决方案。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-07-24
              • 1970-01-01
              • 2019-11-02
              • 2021-02-08
              • 1970-01-01
              • 2020-01-30
              • 1970-01-01
              相关资源
              最近更新 更多