Swift:从数组中选择两个随机但唯一的元素

时间:2019-04-18 08:07:56

标签: ios arrays swift xcode

我要初始化一个数组,如下所示:

func initRoundsArray(playersArray: [String]) -> [String] {

    let rounds: [String] = [
        "ROUND 1: First player: \(String(playersArray.randomElement()!)), Second player: \(String(playersArray.randomElement()!))",
        "ROUND 2: First player: \(String(playersArray.randomElement()!)), Second player: \(String(playersArray.randomElement()!))",
        "ROUND 3: First player: \(String(playersArray.randomElement()!)), Second player: \(String(playersArray.randomElement()!))"
    ]

    return rounds
}

在我的视图控制器中使用以下代码:

let playersArrayInput: [String] = ["Player 1", "Player 2", "Player 3", "Player 4", "Player 5", "Player 6", "Player 7", "Player 8", "Player 9"]
var arrayOfRounds: [String]?

// Called like so in viewDidLoad:
arrayOfRounds = initRoundsArray(playersArray: playersArrayInput)

但是我正在努力弄清楚如何为每个回合选择2个随机且唯一的元素。例如,arrayOfRounds[0]当前可能是"ROUND 1: First player: Player 6, Second player: Player 6"

由于initRoundsArray仅被调用一次(arrayOfRounds随后被突变),所以我认为不宜仅将数组改组并选择前2个元素,因为在这种情况下,每一轮都会与相同的2个玩家在一起。

我不确定如何实现这一目标(或者甚至有可能实现)。理想情况下,所需要做的就是在第一轮选择两名球员时,例如,检查他们是否彼此不同。

5 个答案:

答案 0 :(得分:3)

基本上,您需要从数组中生成n个随机元素,您可以使用以下算法进行操作:

func pick<T>(_ n: Int, from array: [T]) -> [T] {
    var copy = array // make a copy so we can make changes
    var result = [T]()
    for _ in 0..<n {
        let randomElementIndex = Int.random(in: 0..<copy.count) // generate random index
        let randomElement = copy[randomElementIndex]
        copy.remove(at: randomElementIndex) // remove the generated element
        result.append(randomElement) // add it to the result
    }
    return result
}

要生成3个回合的玩家,请用n=6调用。

答案 1 :(得分:1)

您可以创建第二个数组,而不是将值复制到新数组中,而是将元素弹出到新数组中,因此确定要弹出的第二个项肯定是另一个。

例如。

func initRoundsArray(playersArray: [String]) -> [String] {

    var playersArrayCoppy = playersArray
    let round1Item = playersArrayCoppy.remove(at: Int.random(in: 0...(playersArrayCoppy.count - 1)))
    let round2Item = playersArrayCoppy.remove(at: Int.random(in: 0...(playersArrayCoppy.count - 1)))
    let round3Item = playersArrayCoppy.remove(at: Int.random(in: 0...(playersArrayCoppy.count - 1)))

    let rounds: [String] = [
        "ROUND 1: First player: \(round1Item), Second player: \(round1Item)",
        "ROUND 2: First player: \(round2Item), Second player: \(round2Item)",
        "ROUND 3: First player: \(round3Item), Second player: \(round3Item)"
    ]
    return rounds
}

当然,如果不确定数组计数是否> = 3,则需要添加一些检查,以确保数组中有项目

编辑 根据注释,您可能想要的是此功能

func initRoundsArray(roundsNumber: Int, playersArray: [String]) -> [String] {
    var roundsArray:[String] = []
    for i in 1...roundsNumber {
        var playersArrayCoppy = playersArray
        let player1Item = playersArrayCoppy.remove(at: Int.random(in: 0...(playersArrayCoppy.count - 1)))
        let player2Item = playersArrayCoppy.remove(at: Int.random(in: 0...(playersArrayCoppy.count - 1)))

        let round: String = "ROUND \(i): First player: \(player1Item), Second player: \(player2Item)"
        roundsArray.append(round)
    }
    return roundsArray
}

您用initRoundsArray(roundsNumber: 3, playersArray: ["?","?",..."])

对其进行了调用

答案 2 :(得分:1)

我会选择以下内容:

func extractRandomElementsFromArray<Generic>(_ array: [Generic], numberOfElements: Int) -> [Generic]? {
    guard array.count >= numberOfElements else { return nil }

    var toDeplete = array
    var toReturn = [Generic]()

    while toReturn.count < numberOfElements {
        toReturn.append(toDeplete.remove(at: Int.random(in: 0..<toDeplete.count)))
    }

    return toReturn
}

这应该对任何数量的元素在任何数组上有效。基本上,我们从一个数组中删除随机元素,然后将它们填充到另一个数组中,直到第二个数组中有足够的元素为止。

在您的情况下,可以用作:

let playersArrayInput: [String] = ["Player 1", "Player 2", "Player 3", "Player 4", "Player 5", "Player 6", "Player 7", "Player 8", "Player 9"]

let pairArray = extractRandomElementsFromArray(playersArrayInput, numberOfElements: 2)!
let player1 = pairArray[0]
let player2 = pairArray[1]

答案 3 :(得分:0)

我最终使用了以下代码(<ACTION>只是一个占位符):

func initRoundsArray(playersArray: [String]) -> [String] {

    let round1Players = twoRandomPlayers(playersArray: playersArray)
    let round2Players = twoRandomPlayers(playersArray: playersArray)

    let rounds: [String] = [
        "ROUND 1: \(round1players[0]), do <ACTION> to \(round1players[1])",
        "ROUND 2: \(round2players[0]), do <ACTION> to \(round2players[1])",
        "ROUND 3: \(String(playersArray.randomElement()!)), do <ACTION>"
    ]

    return rounds
}

func twoRandomPlayers(playersArray: [String]) -> [String] {
    let shuffledPlayersArray = playersArray.shuffled()

    return Array(shuffledPlayersArray.prefix(2))
}

我每次都会调用一个单独的函数,该函数返回一个数组,其中包含2个playersArray中随机挑选(且唯一)的玩家,并将其用于需要2个玩家的回合中。

我知道这可能不是最好的方法,但是在我的最终实现中,绝大多数回合可能只涉及一名玩家,因此.randomElement()是合适的。

对于原始问题中的措词令人困惑,我深表歉意,但我希望这可以澄清问题,并非常感谢大家的建议/回答:)

答案 4 :(得分:0)

首先,让我们假设playersArrayInput没有任何重复项。

let playersArrayInput: [String] = ["Player 1", "Player 2", "Player 3", "Player 4", "Player 5", "Player 6", "Player 7", "Player 8", "Player 9"]

有效的实现方式是仅对索引进行混洗:

func initRoundsArray(playersArray: [String]) -> [String] {

    var shuffledIndices = playersArray.indices.shuffled()
    let count = playersArray.count - 1
    var rounds = [String]()
    var i = 0

    while i <= count {
        let round = String(rounds.count + 1)
        if i <= count - 1 {
            switch Bool.random() {
            case true:
                rounds.append("ROUND " +
                    round +
                    ": " +
                    playersArray[shuffledIndices[i]] +
                    ", do <ACTION>")
                i += 1
            default:
                rounds.append("ROUND " +
                    round +
                    ": " +
                    playersArray[shuffledIndices[i]] +
                    ", do <ACTION> to " +
                    playersArray[shuffledIndices[i + 1]])
                i += 2
            }
        } else {
            rounds.append("ROUND " +
                round +
                ": " +
                playersArray[shuffledIndices[count]] +
                ", do <ACTION>")
            break
        }
    }

    return rounds
}

我们可以这样打印呼叫结果:

initRoundsArray(playersArray: playersArrayInput).forEach { print($0) }

例如:

ROUND 1: Player 7, do <ACTION> to Player 1
ROUND 2: Player 8, do <ACTION>
ROUND 3: Player 5, do <ACTION>
ROUND 4: Player 9, do <ACTION> to Player 2
ROUND 5: Player 3, do <ACTION> to Player 6
ROUND 6: Player 4, do <ACTION>