【问题标题】:Resolving too many else-if statements in Swift 3解决 Swift 3 中过多的 else-if 语句
【发布时间】:2017-05-28 18:17:57
【问题描述】:

问题:给定函数的输入,测试每个用户以确保他们符合以下条件: 1. users 数组中的每个用户都不能与当前用户共享聊天室。 (Chatroom 对象有两个属性“firstUserId”和“secondUserId”。
2. users 数组中的每个用户都不是当前用户。 3. users 数组中的每个用户都在当前用户半径 5 英里范围内。

在完成处理程序的调用中,我检查用户对象的值是否为真,如果是,我将其作为潜在匹配显示给当前用户。

现在,我很快就暴力破解了这个解决方案,但每次看到它都会畏缩。它似乎非常低效。非常感谢任何有关更优雅解决方案的提示!

typealias validUsersCompletionHandler = (_ users: [User: Bool]) -> Void

private func validateNewUsers(currentUser: User, users: [User], chatrooms: [Chatroom], completionHandler: validUsersCompletionHandler?) {

    var results: [User: Bool] = [:]

    let currentUserCoords = CLLocation(latitude: currentUser.latitude, longitude: currentUser.longitude)

    for user in users {
        let newUserCoords = CLLocation(latitude: user.latitude, longitude: user.longitude)
        let distance = currentUserCoords.distance(from: newUserCoords)
        // // 1 mile = 1609 meters, 8046.72 = 5 miles.
        for chatroom in chatrooms {
            if currentUser.id == chatroom.firstUserId && user.id == chatroom.secondUserId {
                results[user] = false
            } else if currentUser.id == chatroom.secondUserId && user.id == chatroom.firstUserId {
                results[user] =  false
            } else if user.id == currentUser.id {
                results[user] = false
            } else if distance > 8046.72 {
                results[user] = false
            } else {
                results[user] = true
            }
        }
    }
    completionHandler?(results)
}

// *************************************************** ******************************

// 下面是我修改后的方法。我想稍微优雅一点?

// *************************************************** ******************************

typealias validUsersCompletionHandler = (_ users: [User: Bool]) -> Void

private func validateNewUsers(currentUser: User, users: [User], chatrooms: [Chatroom], completionHandler: validUsersCompletionHandler?) {

    var results: [User: Bool] = [:]

    var isInRange = false

    var distance: Double = 0 {
        didSet {
            if distance > 8046.72 {
                isInRange = false
            } else {
                isInRange = true
            }
        }
    }

    let currentUserCoords = CLLocation(latitude: currentUser.latitude, longitude: currentUser.longitude)

    let currentUserId = currentUser.id

    for user in users {

        let userId = user.id

        let newUserCoords = CLLocation(latitude: user.latitude, longitude: user.longitude)

        distance = currentUserCoords.distance(from: newUserCoords)
        // // 1 mile = 1609 meters, 8046.72 = 5 miles.

        for chatroom in chatrooms {

            switch (currentUserId, userId, isInRange) {

            case (chatroom.firstUserId,chatroom.secondUserId, _), (_, _, false),(chatroom.secondUserId, chatroom.firstUserId, _), (_, currentUserId, _): results[user] = false

            default: results[user] = true

            }
        }

    }

    completionHandler?(results)

}

}

【问题讨论】:

    标签: swift if-statement nested-loops


    【解决方案1】:

    您可以将 if 语句替换为 switch... 或者您可以使用 (currentUserId, userId)

     //always check for optionals
        guard let currentUserId = currentUser.id, let userId = user.id, else{
        return
        }
       //The switch should have this format:
        switch (currentUserId, userId){
        //currentUserId == chatroom.firstUserId,  userId = chatroom.secondUserId)
        case (chatroom.firstUserId,chatroom.secondUserId):
        //do your things
        break
        case (chatroom.secondUserId,firstUserId):
        //do other things
        break
        default:
        break
        }
    

    您甚至可以将 case 与声明或比较更多选项一起使用:

        switch value{
        case let x where value > 10:
        //When value is bigger than 10..etc
        default:
        break
        }
    

    为了更好的使用,请参阅:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html

    祝编码愉快:)

    【讨论】:

    • 谢谢。你的方法是有道理的。最困扰我的是必须将所有这些逻辑放在内部 for 循环中,这意味着它将在每个聊天室对象、每个用户对象上执行。我只需要想一些更有效的方法,但我认为你已经充分回答了我的问题,所以我将其核对为正确。
    • 我必须查看您的整个代码,您打算这样做以帮助您,但我认为您可以在 User 类上实现一些功能,该功能可以处理所有设置......但正如我说,我需要查看您的代码...无论如何感谢您标记我的答案! :)
    • 这个方法位于一个名为 UserProvider 的单例类中,它完成了我从数据库中获取用户的大部分工作以及其他工作。我展示的是一个私有的辅助方法。我仍然在努力理解我应该如何根据架构来构建我的对象,并且在我的脑后一直想知道这一切是否应该在 User 类的扩展中。
    • 这里不需要break 语句:一旦达到case 语句的“主体”范围,switch 将中断(默认行为)。
    • 您介意检查该方法的修订版吗?我已经对其进行了测试,并且可以正常工作。但它是解决这个特定问题的可靠方法吗?我问,因为我可能会将整个项目发布到 GitHub 以供潜在雇主查看。
    【解决方案2】:

    或者,您可以将元组值存储在 struc 中的某个位置(应该稍微更改逻辑),但您可以这样做:

    struct myBeautifulCases{
    static let userIsCurrent = (_,currentUserId,_)
    static let sameChatroom = (chatroom.secondUserId, chatroom.firstUserId, _)
    static let sameChatroomAlt = (chatroom.secondUserId, chatroom.firstUserId, _)
    static let isWithinRange = case (_, _, false)
    
    } 
    

    现在你可以用变量名覆盖丑陋的元组,但你应该稍微改变一下逻辑:)祝你好运,项目成功:)

    【讨论】:

    • 感谢您的帮助!最后一个问题。我应该改变逻辑来实现什么?为了避免嵌套for循环,或者让这个方法自己包含在用户对象中?使用给定的函数输入,这是我考虑解决的一般方法。我知道可能需要花更多时间在 HackerRank 上哈哈!
    猜你喜欢
    • 1970-01-01
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多