swift2 是否在数组中查找与Swift中的标准匹配的最近项?

dkqlctbz  于 2022-11-06  发布在  Swift
关注(0)|答案(6)|浏览(198)

当我引用数组中的某个项时,我希望找到与它最接近且符合特定条件(向前或向后)的另一项。
例如,我有这个数组:

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的想法吗?

fv2wmkja

fv2wmkja1#

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

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

Swift的每一个版本都在不断改进,以优化性能和效率。使用高阶函数,在数组值中查找最接近的匹配会更容易。根据您的需要更改值的类型。

bfnvny8b

bfnvny8b2#

伪代码:

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代码。

wi3ka0sx

wi3ka0sx3#

下面是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" })
owfi6suc

owfi6suc4#

除了其他解决方案外,还有一个“一行程序”:

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))

根据大卫Berry的回答:

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))

**注:**从性能Angular 来看,大卫Berry的答案更好。

oyjwcjzk

oyjwcjzk5#

此函数应在以下情况下工作:

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,否则返回该项。
它只是向后搜索,直到找到匹配项,然后向前搜索,直到找到匹配项,并返回最接近的项。

ohfgkhjo

ohfgkhjo6#

最简单的方法是:

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"
}

这并不是问题的完整答案,只是一个向前搜索的例子。在我发布后,我意识到它与问题不匹配,但我会把它作为一个部分例子。

相关问题