-1

I am trying to make an app for my friends and I to randomly decide our draft order for fantasy sports. Right now with my app, when the button is clicked, a random name from the array is selected and displayed on the screen. However, when a name shows up, I would like to have it removed so that it can't appear again. The removeName function has been my latest attempt to try this, but it gives me an error. Does anyone know how to do this? Here is the code I am currently using.

let adam = "Adam"
let connor = "Connor"
let drew = "Drew"
let schwenk = "Schwenk"
let langan = "Langan"
let tram = "Tram"
let trey = "Trey"
let joey = "Joey"
let nate = "Nate"
let goose = "Goose"


var names = [adam, connor, drew, schwenk, langan, tram, trey, joey, nate, goose]

func pickName() -> String {
    let randomName = Int(arc4random_uniform(UInt32(names.count)))
    return names[randomName]
}

func removeName() {
    let randomName = Int(arc4random_uniform(UInt32(names.count)))
    let namesIndex = randomName
    names.remove(at: namesIndex)
}
Vemonus
  • 868
  • 1
  • 16
  • 28
Hrababa
  • 1
  • 1
  • 2
    Please don't post a screenshot of your code – copy and paste your actual code into the question, along with the error(s) you've got. – Hamish Jan 27 '17 at 21:10
  • why not use a dictionary defined as `[String: Bool]` and set the names mapped `boolean` value to true when it has already been chosen. – m_callens Jan 27 '17 at 21:17
  • @m_callens, that would considerably complicate how the random selection is done. You could do the random selection from a filtered array of keys, but if OP is already having problems with removing and adding elements in an array, I think that filtering is a bit tougher than is necessary. My solution is trivially easy. – NRitH Jan 27 '17 at 21:20
  • FYI, declaring a variable for each name string is more work than is needed, unless you're doing something with those variables elsewhere in the code. – NRitH Jan 27 '17 at 21:30
  • 1
    As an alternative approach, you could [shuffle the array](http://stackoverflow.com/questions/24026510) _once_, thereafter simply traverse the array (with no need of further mutating it) one element at a time each time a new name is to be "picked". – dfrib Jan 27 '17 at 22:43

4 Answers4

3

Store two arrays. When a name is selected randomly from array 1, move it from array 1 to array 2. When array 1 is empty, swap the two arrays. (This is what we in the game industry used to call a "random, no-repeat array".)

func pickName() {
    if names.isEmpty() {
        names = selectedNames
        selectedNames = [:]
    }

    let randomIndex = Int(arc4random_uniform(UInt32(names.count)))        
    let name = names.remove(at: randomIndex)   // remove and return in 1 step!
    selectedNames.append(name)
    return name
}
NRitH
  • 13,441
  • 4
  • 41
  • 44
  • Good answer, but could you include some code that will help on the **check* to insure a elected element in array "1" isn't selected again? How can one consider this answer complete? –  Jan 27 '17 at 21:38
  • 1
    Removing my down vote. As I was writing this, you posted the code. Great! And up-voted. –  Jan 27 '17 at 21:39
  • Done. The way you ensure that the same name isn't selected is to remove it from the `names` array after you select it, then put it in the `selectedNames` array instead. – NRitH Jan 27 '17 at 21:39
0

Try something like this based on NRitH's answer

var names = [adam, connor, drew, schwenk, langan, tram, trey, joey, nate, goose]
var selectedNames = [String]()

func pickName() -> String? {
    if names.isEmpty {
        names = selectedNames
        selectedNames = []
        return nil
    } else {
        let randomIndex = Int(arc4random_uniform(UInt32(names.count)))
        let randomName = names[randomIndex]
        removeName(randomName)
        return randomName
    }
}

private func removeName(_ name: String) {
    guard let index = names.index(of: name) else { return }
    names.remove(at: index)
    selectedNames.append(name)
}
Community
  • 1
  • 1
PJayRushton
  • 145
  • 7
  • This is a good start, but to be completely correct, the first line of `pickName()` (or the last line of `removeName()`) needs to swap the two arrays, so `if names.isEmpty() { names = selectedNames; selectedNames = [:] }`. – NRitH Jan 27 '17 at 21:29
  • He didn't specify he wanted the array to reset and be ready to redraw, but yeah, if that's what you wanted to do that would be the way to do it. – PJayRushton Jan 27 '17 at 21:31
  • Actually, there's also a bug in the code you posted. You remove the string at `index` inside `removeName()`, but then after you call `removeName()` in `pickName()`, you try to get the string at that index again. At best, you'll get a different value; at worst, you'll crash when the array is empty. – NRitH Jan 27 '17 at 21:32
  • Thanks [NRitH](http://stackoverflow.com/users/665456/nrith) for catching that. I updated my answer. – PJayRushton Jan 27 '17 at 21:46
0

The method .remove(at: x) removes the item at the specific index, also it return the item. Than you can store the removed values in an array which is called alreadyDrafted, than use NRitH suggestion.

var name        = ["Adam", "Connor", "Drew", ... "Goose"]
var pickedNames = [String]()    


func pickName() -> String {

    let randomIndex = Int(arc4random_uniform(UInt32(names.count - 1))) // you get a Number which you use later as an Index, you don't get the name

    let pickedName = names.remove(at: randomIndex)


    pickedNames.insert(pickedName, at = 0)

    if( names.empty ) {

         names = pickedNames // reset the array

    }

    return pickedName

}

The function now removes and return the picked name.

When you use .count() remember that it return the number of items in the array. But an Array starts at 0, which means that the 1 (first) items is stored at 0

 let firstItem = array[0] 

these leads us to the knowledge that the last item is at .count() - 1. You would never picked adam and receive an error when randomIndex = 10, because the last value is stored at 9.

Take a look at the apple docs which are very helpful. Swift Programming Language: Collection Types

Aritmetic
  • 1
  • 5
  • Why are you removing the item at `randomIndex - 1`? `arc4random_uniform()` return values 0 <= *x* < *n*, so if it returns `0`, you'll blow up when you try to get the element at element `-1`. – NRitH Jan 27 '17 at 22:24
  • Haven't seen it, thank you for mention it. I changed it, so the random number can only picked in the area from 0 to 9. – Aritmetic Jan 27 '17 at 22:37
0

A slight variation of what you asked:

func ranomizeElements(elements: [String]) -> [String]
{
    var allElements = elements
    var randomlyOrderedElements: [String] = []
    while allElements.count > 0
    {
        let randomIndex = Int(arc4random_uniform(UInt32(allElements.count)))
        let randomElement = allElements.remove(at: randomIndex)
        randomlyOrderedelEments.append(randomElement)
    }
    return randomlyOrderedElements
}

You can keep a static array of your players and keep calling that func on it. Just iterate the result of the func for each round of the draft.

ghostatron
  • 2,620
  • 23
  • 27