【问题标题】:Updating array values using higher order functions in Swift在 Swift 中使用高阶函数更新数组值
【发布时间】:2018-09-10 01:51:34
【问题描述】:

我在使用新的 map、flatMap、filter、reduce 和 zip 函数时遇到了困难。考虑以下: 您有两个数组 A 和 B,它们包含不同的对象。对于 A 中的每个对象,您需要在 B 中找到相应的对象(通过它们的 id 属性),并从 B 更新对象的一些属性。这可以用旧方法完成,使用两个 for 循环,如下所示:

private func update(statuses: [JobStatus], forJobs jobs: [JobBookPayload]) {
    for jobStatus in statuses {
        for job in jobs {
            if jobStatus.jobId == job.jobId {
                job.status = jobStatus.status!
                job.option = jobStatus.option!
            }
        }
    }
}

这是否可以使用新函数来完成,以使代码更“Swifty”并提高可读性?

【问题讨论】:

  • 清晰易读的代码。 – 除非您更改数据结构,否则您将始终需要嵌套循环(在过滤器中显式或隐藏)。
  • 还不错,我只是想知道是否可以使用 Swift 中的新功能来实现。

标签: ios arrays swift closures


【解决方案1】:

我想既然您使用的是类,您实际上并不想从旧对象创建一个新对象。我相信这可以使用mapfirst 方法来实现:

@discardableResult
func update(statuses: [JobStatus], forJobs jobs: [JobBookPayload]) -> [JobBookPayload] {
    return jobs.map({ (payload) -> JobBookPayload in
        if let status = statuses.first(where: { payload.jobId == $0.jobId }) {
            payload.status = status.status
            payload.option = status.option
        }
        return payload
    })
}

使用@discardableResult 将允许您使用此函数并忽略生成的数组 - 初始数组中的对象无论如何都会发生变异。

【讨论】:

  • 如果你还是要丢弃结果,为什么要让函数返回任何东西?难道不能这样做:_ = jobs.map({ ..., 并且有没有返回值的函数?
  • @damjandd 对此无可争辩,但这种方法实际上是否更好?
  • 不是真的,我只是好奇如何使用新技术完成它,以及它是否会使代码更具可读性。感谢您的意见。
【解决方案2】:

for .. in .. { if conditional { ... } } 替换为for .. in .. where conditional { ... }

您可以通过在内部 for 循环中引入 where 子句来替换其主体的单个 if 语句来进行小规模重构:

private func update(statuses: [JobStatus], forJobs jobs: [JobBookPayload]) {
    for jobStatus in statuses {
        for job in jobs where jobStatus.jobId == job.jobId {
            job.status = jobStatus.status!
            job.option = jobStatus.option!
        }
    }
}

【讨论】:

    【解决方案3】:

    “新”(功能)方式的想法是从前一个对象创建一个新对象:

    private func update(statuses: [JobStatus], for jobs: [JobBookPayload]) -> [JobBookPayload] {
        return jobs.reduce([]) { newJobs, job in
            return newJobs + statuses.flatMap { status in
                return status.jobId == job.jobId ?
                    //Construct a new object
                    JobBookPayload(status: status.status, option: status.option) : nil
            }
        }
    }
    

    假设有一个status对象对应一个job

    private func update(statuses: [JobStatus], for jobs: [JobBookPayload]) -> [JobBookPayload] {
        return jobs.reduce([]) { newJobs, job in
            return newJobs + statuses.lazy.filter { $0.jobId == job.jobId }.first.flatMap { 
                JobBookPayload(status: $0.status, option: $0.option) // Construct the object
            }
        }
    }
    

    避开reduce

    private func update(statuses: [JobStatus], for jobs: [JobBookPayload]) -> [JobBookPayload] {
        return jobs.flatMap { job in
            return statuses.flatMap { status in
                return status.jobId == job.jobId ?
                    //Construct a new object
                    JobBookPayload(status: status.status, option: status.option) : nil
            }.last
        }
    }
    

    【讨论】:

    • 看起来好像您正在使用reduce 作为“地图替换”,因为创建了大量中间数组,所以效率极低。比较airspeedvelocity.net/2015/08/03/…
    • 是的,对,我也可以在这里使用flatMap,有时我更喜欢reduce 以提高可读性,此外,最近还有另一篇文章删除了reduce 的指数部分,它的线性度是map 的 1.5 倍
    • 你有那篇文章的链接吗? – 此外,这个 github.com/apple/swift-evolution/blob/master/proposals/… 应该改进可以在 Swift 4 中提供帮助。
    • 您的第二个代码无法编译。新建JobBookPayload需要保持jobId等属性不变。您的代码返回一个新数组,其中删除了一些元素,这是完全错误的。原始代码从不删除 JobBookPayload 数组中的任何元素。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多