John Mack
John Mack

Reputation: 103

Sorting an array with nested tuples

I'm creating a leaderboard of top 5 scores that contains the following elements:

Player Name

Completion Time (how long it took to complete the game)

Total Moves

Date Stamp

To do this, I have created an array of tuples with a nested tuple to track minutes, seconds, and milliseconds as seen below:

var leaderBoard: [(playerName: String, completionTime: (minutes: Int, seconds: Int, miliseconds: Int), totalMoves: Int, dateStamp: Date)] = []

When a game is completed, these values are appended to the array until it contains a total of 5 elements.

My trouble is that I need to sort this array by completion time in ascending order. Due to the complicated, nested nature of the tuples in this array, I can't seem to find a valid way of accomplishing this. Any assistance would be greatly appreciated.

Upvotes: 0

Views: 237

Answers (3)

vacawama
vacawama

Reputation: 154583

If your completionTime is well formed (seconds is in 0...59 and milliseconds is in 0...999), then you can sort your leaderboard with:

leaderBoard.sort { $0.completionTime < $1.completionTime }

This works because Swift can compare 2 tuples (1, 2, 3) and (1, 2, 4) with < and it will compare the first items, and if they're equal, it will compare the second items, and if they're equal it will compare the third. So you can order them with a simple < comparison. This works even if the items are labelled as long as the two tuples have the same number of elements, the number of elements are 6 or fewer, and the types of the corresponding elements match.


Example:

var leaderBoard: [(playerName: String, completionTime: (minutes: Int, seconds: Int, milliseconds: Int), totalMoves: Int, dateStamp: Date)] = [
    (playerName: "Fred", completionTime: (minutes: 4, seconds: 10, milliseconds: 800), totalMoves: 3, dateStamp: Date()),
    (playerName: "Barney", completionTime: (minutes: 5, seconds: 10, milliseconds: 800), totalMoves: 3, dateStamp: Date()),
    (playerName: "Wilma", completionTime: (minutes: 4, seconds: 10, milliseconds: 801), totalMoves: 3, dateStamp: Date()),
    (playerName: "Bam Bam", completionTime: (minutes: 1, seconds: 10, milliseconds: 0), totalMoves: 3, dateStamp: Date()),
    (playerName: "Pebbles", completionTime: (minutes: 4, seconds: 10, milliseconds: 799), totalMoves: 3, dateStamp: Date())
]

leaderBoard.sort { $0.completionTime < $1.completionTime }
leaderBoard.forEach { print($0) }

Output:

(playerName: "Bam Bam", completionTime: (minutes: 1, seconds: 10, milliseconds: 0), totalMoves: 3, dateStamp: 2018-09-21 11:17:36 +0000)
(playerName: "Pebbles", completionTime: (minutes: 4, seconds: 10, milliseconds: 799), totalMoves: 3, dateStamp: 2018-09-21 11:17:36 +0000)
(playerName: "Fred", completionTime: (minutes: 4, seconds: 10, milliseconds: 800), totalMoves: 3, dateStamp: 2018-09-21 11:17:36 +0000)
(playerName: "Wilma", completionTime: (minutes: 4, seconds: 10, milliseconds: 801), totalMoves: 3, dateStamp: 2018-09-21 11:17:36 +0000)
(playerName: "Barney", completionTime: (minutes: 5, seconds: 10, milliseconds: 800), totalMoves: 3, dateStamp: 2018-09-21 11:17:36 +0000)

Upvotes: 2

Duncan C
Duncan C

Reputation: 131418

The sorted function does this job nicely. It takes a closure that compares 2 items from the array and returns true if the first is less than the second.

You just need to write a closure that calculates a value for each completionTime and compares them. It's faster to do integer math, so the following converts each completionTime to integer milliseconds and compares those values:

let sortedPlayers = leaderBoard.sorted { lhs, rhs in
    let lhTime =  lhs.completionTime.minutes * 60_000 + lhs.completionTime.seconds * 1000 + 
      lhs.completionTime.miliseconds
    let rhTime =  rhs.completionTime.minutes * 60_000 + rhs.completionTime.seconds * 1000 + 
      rhs.completionTime.miliseconds
    return lhTime < rhTime
}

Upvotes: 0

John Mack
John Mack

Reputation: 103

func recordWin() {
    let newEntry: (playerName: String, completionTime: (minutes: Int, seconds: Int, miliseconds: Int), totalMoves: Int, dateStamp: Date) = (playerName, completionTime, totalMoves, dateStamp)

    if leaderBoard.count < 5 {
        leaderBoard.append(newEntry)
    }

    else {

    }

    if leaderBoard.count > 1 {
        for _ in 0...4 {
            var currentIndex = 0

            for score in leaderBoard {
                if currentIndex > 0 {
                    if score.completionTime.minutes < leaderBoard[currentIndex - 1].completionTime.minutes {
                        leaderBoard.remove(at: currentIndex)
                        leaderBoard.insert(score, at: currentIndex - 1)
                    }

                    else if score.completionTime.minutes == leaderBoard[currentIndex - 1].completionTime.minutes {
                        if score.completionTime.seconds < leaderBoard[currentIndex - 1].completionTime.seconds {
                            leaderBoard.remove(at: currentIndex)
                            leaderBoard.insert(score, at: currentIndex - 1)
                        }

                        else if score.completionTime.seconds == leaderBoard[currentIndex - 1].completionTime.seconds {
                            if score.completionTime.miliseconds < leaderBoard[currentIndex - 1].completionTime.seconds {
                                leaderBoard.remove(at: currentIndex)
                                leaderBoard.insert(score, at: currentIndex - 1)
                            }
                        }
                    }
                }

                currentIndex += 1
            }
        }
    }

    if leaderBoard.count > 5 {
        leaderBoard.removeLast()
    }

    for score in leaderBoard {
        print(score.playerName + ": " + "Completion Time: " + "Total Moves: " + score.totalMoves.description + "Completion Time: " + score.completionTime.minutes.description + ":" + score.completionTime.seconds.description + ":" + score.completionTime.miliseconds.description + " - " + score.dateStamp.description)
    }
}

Upvotes: 0

Related Questions