【问题标题】:Count elements of array matching condition in Swift在Swift中计算数组匹配条件的元素
【发布时间】:2015-10-05 19:21:36
【问题描述】:

我基本上是在寻找以下 c++ 代码的 swift 等效项:

std::count_if(list.begin(), list.end(), [](int a){ return a % 2 == 0; }); // counts instances of even numbers in list

当然,我的问题实际上并不是搜索偶数;只是计算符合条件的实例的一般情况。

我还没有看到内置函数,但很想知道我只是错过了它。

【问题讨论】:

    标签: swift


    【解决方案1】:

    像这样:

     let a: [Int] = ...
     let count = a.filter({ $0 % 2 == 0 }).count
    

    【讨论】:

    • 完美,这正是我想要的。将在 ~9 分钟内接受。
    • 您可以消除闭包周围的括号,使其成为尾随闭包。
    • 另外,这将有一个 O(n) 空间分配,是否有一个维护 O(1) 空间的衬垫?
    • @AidanGomez 此答案的作者应在filter 之前放置lazy,以避免O(n) 空间分配
    • @AidanGomez 如果答案有效,请接受答案:)
    【解决方案2】:

    Aderstedt 版本的替代方案

    let a = [ .... ]
    let count = a.reduce(0){ 
        (count, element) in 
        return count + 1 - element % 2
    }
    

    我的直觉说我的方式会更快,因为它不需要创建第二个数组。但是,您需要对这两种方法进行概要分析才能确定。

    编辑

    根据 MartinR 关于函数泛化的评论,这里是

    extension SequenceType
    {
        func countMatchingCondition(condition: (Self.Generator.Element) -> Bool) -> Int
        {
            return self.reduce(0, combine: { (count, e) in count + (condition(e) ? 1 : 0) })
        }
    }
    
    let a = [1, 2, 3, 3, 4, 12].countMatchingCondition { $0 % 2 == 0 }
    print("\(a)") // Prints 3
    

    【讨论】:

    • @MartinR 我最初将这一行写成return count + element % 2 == 0 ? 1 : 0,因为它是一样的,但避免将布尔值转换为整数。
    • 我删除了我之前的评论,因为我注意到Int(element % 2 == 0) 实际上是从布尔值创建了一个NSNumber 对象。 – 我想说的是,您的解决方案不适用于更一般的谓词。
    • 这是我简单测试中最快的解决方案 :)
    【解决方案3】:

    默认数组:

    let array: [Int] = [10, 10, 2, 10, 1, 2, 3]
    
    1. filter(_:)方法

    let countOfTen = array.filter({ $0 == 10 }).count // 3

    1. count(where:)方法

    更新:这个 Swift 5.0 功能在 beta 测试中被撤回,因为它会导致类型检查器出现性能问题。

    let countOfTen = array.count(where: { $0 == 10 }) // 3
    

    【讨论】:

    • count(where:) 是真的吗?它在 Swift 5 中对我不起作用,您提供的链接是 404(找不到页面)。
    • 好的,看起来count(where:) 在最后一分钟从 Swift 5 中删除了。希望它会在未来的版本中重新出现。
    • 这个 Swift 5.0 特性在 beta 测试中被撤回,因为它会导致类型检查器的性能问题。希望它会在 Swift 5.1 中及时回归,也许会用一个新名称来避免出现问题。 hackingwithswift.com/articles/126/whats-new-in-swift-5-0
    【解决方案4】:

    您可以使用 Collection.lazy 来获得 Aderstedt 答案的简单性,但空间为 O(1)。

    let array = [1, 2, 3]
    let count = array.lazy.filter({ $0 % 2 == 0 }).count
    

    【讨论】:

      【解决方案5】:

      执行此操作的最紧凑的 reduce 语句是:

      let a = Array(1 ... 20)
      let evencount = a.reduce(0) { $0 + ($1 % 2 == 0 ? 1 : 0) }
      

      Reduce 采用两个变量:从 0 (var $0) 开始,然后对于 Array a (var $1) 中的每个元素,如果该值可被 2 整除且没有余数,则在计数中加一。

      这也很有效,因为它不像使用 a.filter(){}.count 那样创建额外的数组。

      【讨论】:

        【解决方案6】:

        你也可以用 reduce() 来做到这一点

        let a = Array(1 ... 20)
        let evenCount = a.reduce(0) { (accumulator, value) -> Int in
            guard value % 2 == 0 else { return accumulator }
            return accumulator + 1
        }
        

        几乎所有你想用map()filter 函数做的事情实际上都可以用reduce 完成,尽管它并不总是最易读的。

        【讨论】:

        • 如果最后一行是return accumulator + 1,你就不需要写var
        • @JeremyP 我需要阅读 reduce 但这些似乎是另一个不错的选择。
        【解决方案7】:

        Swift 5 或更高版本:

        public extension Sequence {
            func occurrences(where predicate: (Element) throws -> Bool) rethrows -> Int {
                try reduce(0) { try predicate($1) ? $0 + 1 : $0 }
            }
        }
        

        public extension Sequence where Element: Equatable {
            func occurrences(of element: Element) -> Int {
                reduce(0) { element == $1 ? $0 + 1 : $0 }
            }
        }
        

        let multiplesOf2 = [1,2,3,4,4,5,4,5].occurrences{$0.isMultiple(of: 2)}  // 4
        "abcdeabca".occurrences(of: "a")  // 3
        

        extension BinaryInteger {
            var isOdd: Bool { !isMultiple(of: 2) }
            var isEven: Bool { isMultiple(of: 2) }
        }
        

        (-4).isOdd // false
        (-3).isOdd // true
        (-2).isOdd // false
        (-1).isOdd // true
        0.isOdd    // false
        1.isOdd    // true
        2.isOdd    // false
        3.isOdd    // true
        4.isOdd    // false
        
        (-4).isEven // true
        (-3).isEven // false
        (-2).isEven // true
        (-1).isEven // false
        0.isEven    // true
        1.isEven    // false
        2.isEven    // true
        3.isEven    // false
        4.isEven    // true
        

        let odds = [1,2,3,4,4,5,5,11].occurrences(where: \.isOdd) // 5
        let evens = [1,2,3,4,4,5,5,11].occurrences(where: \.isEven) // 3
        

        【讨论】:

          猜你喜欢
          • 2021-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多