#ios #arrays #swift #xcode
#iOS #массивы #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 игрока.
Я не уверен, как этого добиться (или если это вообще возможно). В идеале все, что необходимо, — это, например, при выборе двух игроков для раунда 1 проверяется, отличаются ли они друг от друга.
Комментарии:
1. перетасуйте массив, а затем выберите элементы
2. @dahiya_boy Я обсуждал этот подход в самом вопросе, предпоследний абзац.
3. вам нужно перетасовывать один раз не для каждого раунда
4. @dahiya_boy Каждый раз может быть разное количество игроков, массив, приведенный в вопросе, является лишь примером. Если бы я должен был перемешать, как вы предлагаете, как я мог бы продолжать предотвращать дублирование?
5. @chumps52 Ваш вопрос немного сбивает с толку. Таким образом, в одном раунде должны участвовать 2 разных игрока. Но тогда вы хотите, чтобы уникальные пары прошли как можно больше раундов. И игроки могут меняться во время исполнения. Это то, что вы пытаетесь сделать? Например, запускается сервер, и люди начинают входить и выходить из системы, поэтому вы хотите продолжать создавать пары игроков для каждого раунда, какие бы вы хотели, чтобы пары не дублировались?
Ответ №1:
В основном вам нужно сгенерировать 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. Это
Int.random(in: 0..<n)
выглядит неправильно, поскольку вы истощаете элементы изcopy
. Предположим, что у него было 2 элемента иn
равно 2. Первый элемент правильный, но второй имеет 50%-ную вероятность отката индекса1
, но единственным допустимым индексом остается 0, посколькуcopy
теперь в нем осталось только 1 элемент.2. @Kamran ой! Исправлено.
Ответ №2:
Я бы выбрал что-то вроде следующего:
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:
Вы можете создать второй массив и вместо того, чтобы копировать значение в новый массив, поместить элемент в новый массив, чтобы вторые элементы, которые вы будете открывать, наверняка были другими.
Пример.
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: ["?","?",..."])
Комментарии:
1. Я только что запустил этот код, и он гарантирует дублирование:
["ROUND 1: First player: Player 9, Second player: Player 9", "ROUND 2: First player: Player 8, Second player: Player 8", "ROUND 3: First player: Player 7, Second player: Player 7"]
2. нет, это не так, я просто добавил round1Item к первому игроку и второму игроку! Butt round1Item всегда отличается от round2Item и round3Item Я не понял, что вы хотите другого для каждого игрока, я думал, что вы хотите для каждого раунда. Если вы хотите, чтобы каждый игрок отличался, вам нужно вызывать функцию для каждого раунда и использовать только 2 предмета
Ответ №4:
В итоге я использовал следующий код ( <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()
подходит.
Я прошу прощения, если формулировка в исходном вопросе была запутанной, но я надеюсь, что это прояснит ситуацию, и большое спасибо всем за их предложения / ответы 🙂
Комментарии:
1. Почему
ROUND 3
в нем есть только один игрок?2. @ielyamani Я кратко упомянул об этом в ответе, но в реальной реализации в некоторых раундах будет задействован только один игрок, который должен что-то сделать. В исходном вопросе об этом не упоминалось, поскольку я уже знал, как это реализовать — у меня был вопрос о получении 2 не дубликатов в одной строке / раунде.
Ответ №5:
Прежде всего, давайте предположим, что 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>