在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]
我想知道是否有更简单或直接的方法来做到这一点。
答案 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]
要计算局部最小值的索引,我们可以迭代a
,a.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)
}
}
}