how to find/filter the array element with the smallest positive difference

时间:2015-09-14 15:20:00

标签: arrays swift sorting filter

Using Swift, I am trying to workout a complex array filter or sort and I am stuck. My array of integers can contain between 1 and 7 elements. Given a certain integer value (say X) which is not in the array, I would like to find the array element that gives the smallest difference between itself and X.

7 个答案:

答案 0 :(得分:33)

Swift 2 中,您可以将其作为具有功能风格的“单行” 编程:

let numbers = [ 1, 3, 7, 11]
let x = 6

let closest = numbers.enumerate().minElement( { abs($0.1 - x) < abs($1.1 - x)} )!

print(closest.element) // 7 = the closest element
print(closest.index)   // 2 = index of the closest element

enumerate()与所有数组元素一起迭代 相应的索引,minElement()返回“最小” 关于封闭的(index, element)对。 闭包比较差值的绝对值 要x的两个元素。

(这里假设数组不为空,因此minElement() 不会返回nil。)

请注意,这可能不是大型阵列的最快解决方案, 因为(几乎)全部计算两次绝对差值 数组元素。但是对于小阵列而言,这无关紧要。

Swift 3

let numbers = [ 1, 3, 7, 11]
let x = 6
let closest = numbers.enumerated().min( by: { abs($0.1 - x) < abs($1.1 - x) } )!
print(closest.element) // 7
print(closest.offset) // 2

可以在编辑历史记录中找到 Swift 1.2 版本。

答案 1 :(得分:3)

Swift 4.2

您可以使用first(where:)firstIndex(where:)数组方法。

假设numbers数组已排序:

let numbers = [1, 3, 7, 11]

let x = 6
let index = numbers.firstIndex(where: { $0 >= x })!
let result = numbers.first(where: { $0 >= x })!

print(index, result) // 2 7

答案 2 :(得分:2)

另一种选择是使用reduce方法

let numbers = [1, 3, 7, 11]
let x = 11

let result = numbers.reduce(numbers.first!) { abs($1 - x) < abs($0 - x) ? $1 : $0 }

如果你想避免重复计算绝对值,你可以先将每个数组元素映射到它与输入数字的绝对差值,然后找到最小值。

let numbers = [1, 3, 7, 11]
let x = 11

if let (index, _) = numbers.map({ abs($0 - x) }).enumerate().minElement({ $0.1 < $1.1 }) {
    let result = numbers[index]
}

答案 3 :(得分:1)

Loop through the values in your array, calculate the abs() difference, compare it to a running 'currentSmallestDifference' variable, if the current difference is smaller than that then overwrite it with the new value, at the same time keep a record of the array index...

int currentSmallestDifference = int.max(); //largest value defined by the int type, this way the first difference in the loop will never be larger than this 
int index = -1;   
for(int i = 0, i < array.size(), i++){
    if( abs(array(i) - X) < currentSmallestDifference){
        currentSmallestDifference = abs(array(i) - X);
        index = i;
    }
}

This solves it in O(n).

But if the array was already sorted (O(nlog(n)), you could then do a O(log(n)) binary search for X in the array and find the closest value to it. No need to use abs() at all in that case... just comparisons

But for array sizes 1 to 7, algorithmic complexity isn't really a factor.

In fact for array size 1, the question isn't even a factor (?!?)

Update>> Just realised the title said smallest positive difference.. So just ditch all the abs() stuff and make sure (array(i) - X) is in the correct sign/direction.

答案 4 :(得分:1)

将其包装成扩展名:

extension Sequence where Iterator.Element: SignedNumeric & Comparable {

    /// Finds the nearest (offset, element) to the specified element.
    func nearestOffsetAndElement(to toElement: Iterator.Element) -> (offset: Int, element: Iterator.Element) {

        guard let nearest = enumerated().min( by: {
            let left = $0.1 - toElement
            let right = $1.1 - toElement
            return abs(left) <= abs(right)
        } ) else {
            return (offset: 0, element: toElement)
        }

        return nearest
    }

    func nearestElement(to element: Iterator.Element) -> Iterator.Element {
        return nearestOffsetAndElement(to: element).element
    }

    func indexOfNearestElement(to element: Iterator.Element) -> Int {
        return nearestOffsetAndElement(to: element).offset
    }

}

答案 5 :(得分:0)

这里是执行单个循环以计算最接近的正值的方法。不是单线的。但仍然有效

let values = [1, 4, 9, 3, 8, 2, 11]
let x = 8

func closestPositiveValue(from array: [Int], value: Int) -> (Int, Int) { // -> (value, difference)
  var (val, dif): (Int?, Int?) = (nil, nil)
  for index in 0..<array.count {
    if x <= array[index] {
      var difference = x - array[index]
      if difference < 0 {
        difference = array[index] - x
      }
      if val == nil { // nil check for initial loop
        (val, dif) = (array[index], difference)
      } else {
        if difference < dif! {
          (val, dif) = (array[index], difference)
        }
      }
    }
  }
  return (val ?? 0, dif ?? 0) // defaults to first item in array if there is no closest positive number
}
print(closestPositiveValue(from: values, value: x)) // prints (8, 0)

答案 6 :(得分:0)

基于Martin R不久前的正确答案,这是我一直在使用的便捷扩展。目前在Swift 5中。

extension Array where Element: (Comparable & SignedNumeric) {

    func nearest(to value: Element) -> (offset: Int, element: Element)? {
        self.enumerated().min(by: {
            abs($0.element - value) < abs($1.element - value)
        })
    }
}

用法:

let numbers = [ 1, 3, 7, 11]
let x = 6

if let closest = numbers.nearest(to: x)
    print(closest)
}
// prints (offset: 2, element: 7)