Swift:遍历数组元素并访问上一个和下一个元素

时间:2018-03-25 13:37:33

标签: swift

在Swift中,我想循环一个数组并将每个元素与前一个和/或下一个元素进行比较。对于每次比较,我将生成一个新元素或什么也不做。是否有"功能性"这样做的方式?

一个例子可能是我有一个Int数组,想要找到所有"本地最小值。

我可以像这样顺序完成

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
var i = 1
var r: [Int] = []

while i < a.count - 1 {
    if a[i] < a[i+1] && a[i] < a[i-1] {
        r.append(i)
    }
    i += 1
}

print(r)
// [6, 11]

我想知道是否有更简单或直接的方法来做到这一点。

7 个答案:

答案 0 :(得分:3)

通常,可以使用dropFirst()zip()遍历相邻的数组元素 在平行下。这是一个简单的例子,它产生了一个增量数组 数组元素:

let a = [ 1, 2, 2, 3, 5, 4, 2, 5, 7, 9, 5, 3, 8, 10 ]

let diffs = zip(a.dropFirst(), a).map(-)
print(diffs)
// [1, 0, 1, 2, -1, -2, 3, 2, 2, -4, -2, 5, 2]

要计算局部最小值的索引,我们可以迭代aa.dropFirst()a.dropFirst(2)并行。 enumerated()用于跟踪 使用数组偏移量和flatMap()(在Swift 4.1中重命名为compactMap()) 只选择那些与当地最小值相对应的指数:

let a = [ 1, 2, 2, 3, 5, 4, 2, 5, 7, 9, 5, 3, 8, 10 ]

let localMins = zip(a.enumerated().dropFirst(), zip(a, a.dropFirst(2))).flatMap {
    $0.element < $1.0 && $0.element < $1.1 ? $0.offset : nil
}
print(localMins) // [6, 11]

答案 1 :(得分:1)

您可以使用while循环和i替换for循环和stride

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
var r: [Int] = []

for i in stride(from: 1, to: a.count - 1, by: 1) {
    if a[i] < a[i+1] && a[i] < a[i-1] {
        r.append(i)
    }
}

print(r)
// [6, 11]

您可以使用过滤器获得真正的幻想,但这并不像上面的代码那样可读:

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.enumerated().dropFirst().dropLast().filter { $0.1 < a[$0.0 + 1] && $0.1 < a[$0.0 - 1] }.map { $0.0 }
print(r)
// [6, 11]

答案 2 :(得分:0)

你也可以迭代索引并像这样比较,

for i in a.indices.dropFirst().dropLast()
{
    if a[i] < a[a.index(after: i)],
            a[i] < a[a.index(before: i)] {
        r.append(i)
    }
}
print(r)
// [6, 11]

或者,像这样,

let result = a.indices.dropLast().dropFirst().filter { i in
    return a[i] < a[a.index(after: i)] &&
            a[i] < a[a.index(before: i)]
}
print(r)
// [6, 11]

或者,简而言之,

let result = a.indices.dropLast()
                      .dropFirst()
                      .filter { a[$0] < a[$0 + 1] &&
                                a[$0] < a[$0 - 1] }
 print(result)

答案 3 :(得分:0)

使用flatMap

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.enumerated().flatMap { (_ offset: Int, _ element: Int) -> Int? in
    guard offset > 0 else { return nil }
    if element < a[offset-1] && element < a[offset+1] {
        return offset
    }
    return nil
}

答案 4 :(得分:0)

该范围内的简单for循环是否具有足够的可读性和可维护性?您可以在迭代时缓存中间值,以便在每次迭代中只访问数组的一个元素。如果你想为任何类似的类型推广它,你可以将它实现为Array的扩展:

extension Array where Element: Comparable {

    func localMinimums() -> [Int] {
        var minimums = [Int]()

        var currentValue = self[0]
        var nextValue = self[1]
        for index in 1..<(self.count - 1) {
            let previousValue = currentValue
            currentValue = nextValue
            nextValue = self[index + 1]
            if currentValue < nextValue && currentValue < previousValue {
                minimums.append(index)
            }
        }

        return minimums
    }
}

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]
let r = a.localMinimums()
print(r)
// [6, 11]

答案 5 :(得分:0)

我认为Martin R的答案很聪明,尽管我尝试使用其他方式回答。

let a = [ 1,2,2,3,5,4,2,5,7,9,5,3,8,10 ]

func withPrevAndNext<T, U>(`default`: U, _ f: @escaping (T, T, T) -> (U)) -> (T) -> (U) {
    var previous: T?
    var current: T?

    return { next in
        defer { (previous, current) = (current, next) }
        guard let prev = previous, let curt = current else { return `default` }
        return f(prev, curt, next)
    }
}

let r = a.enumerated().compactMap(withPrevAndNext(default: .none) { prev, curt, next -> Int? in
    curt.1 < prev.1 && curt.1 < next.1 ? curt.0 : .none
})

print(r)
// [6, 11]

答案 6 :(得分:0)

我正在寻找原始Q的变体,希望对其他人有帮助。我需要映射数组中的每个项目,同时考虑上一个和下一个值:

answer = [letters[char] if char in letters else -1 for char in s]

与原始Q结合使用的不太好方法:

extension Sequence {
    var withPreviousAndNext: [(Element?, Element, Element?)] {
        let optionalSelf = self.map(Optional.some)
        let next = optionalSelf.dropFirst() + [nil]
        let prev = [nil] + optionalSelf.dropLast()
        return zip(self, zip(prev, next)).map {
            ($1.0, $0, $1.1)
        }
    }
}