Get a random unique element from an Array until al

2020-02-07 13:55发布

问题:

I have the following array:

var notebookCovers = ["cover1", "cover2", "cover3", "cover4", "cover4", "cover6", "cover7", "cover8", "cover9", "cover10"]

and a UIButton that when it's pressed it generates a new UIImage with one of the elements of the array.

What I need to do is every time the button is tapped to generate random but unique element from the array (without repeating the elements) until they've all been selected and then restart the array again.

So far, I have it getting a random element but it's repeated and I cannot figure out how to it so it gets a unique image every time

func createNewNotebook() {
    let newNotebook = Notebook()
    let randomInt = randomNumber()
    newNotebook.coverImageString = notebookCovers[randomInt]
    notebooks.insert(newNotebook, at: 0)
    collectionView.reloadData()
}

func randomNumber() -> Int {
    var previousNumber = arc4random_uniform(UInt32(notebookCovers.count))   
    var randomNumber = arc4random_uniform(UInt32(notebookCovers.count - 1)) 
    notebookCovers.shuffle()
    if randomNumber == previousNumber {
        randomNumber = UInt32(notebookCovers.count - 1)
    }
    previousNumber = randomNumber
    return Int(randomNumber)
}

回答1:

Copy the array. Shuffle the copy. Now just keep removing the first element until the copy is empty. When it is empty, start over.

Example:

let arr = [1,2,3,4,5]
var copy = [Int]()
for _ in 1...30 { // just to demonstrate what happens
    if copy.isEmpty { copy = arr; copy.shuffle() }
    let element = copy.removeFirst() ; print(element, terminator:" ")
}
// 4 2 3 5 1 1 5 3 2 4 4 1 2 3 5 1 4 5 2 3 3 5 4 2 1 3 2 4 5 1


回答2:

Set is a collection type that holds unique elements. Converting your notebooks array to Set also lets you take advantage of its randomElement function

var aSet = Set(notebooks)

let element = aSet.randomElement()
aSet.remove(element)


回答3:

If you want to create a looping solution:

let originalSet = Set(arrayLiteral: "a","b","c")
var selectableSet = originalSet

func repeatingRandomObject() -> String {
    if selectableSet.isEmpty {
        selectableSet = originalSet
    }

    return selectableSet.remove(selectableSet.randomElement()!)!
}

force unwrapping is kind of safe here, since we know that the result will never be nil. If you don't want to force unwrap:

let originalSet = Set(arrayLiteral: "a","b","c")
var selectableSet = originalSet

func repeatingRandomObject() -> String? {
    if selectableSet.isEmpty {
        selectableSet = originalSet
    }

    guard let randomElement = selectableSet.randomElement() else { return nil }
    return selectableSet.remove(randomElement)
}


回答4:

You can try something like this,

var notebookCovers = ["cover1", "cover2", "cover3", "cover4", "cover4", "cover6", "cover7", "cover8", "cover9", "cover10"]
var tappedNotebooks: [String] = []
func tapping() {
    let notebook  =  notebookCovers[Int.random(in: 0...notebookCovers.count - 1)]
    if tappedNotebooks.contains(notebook){
        print("already exists trying again!")
        tapping()
    } else {
        tappedNotebooks.append(notebook)
        print("appended", notebook)
    }
    if tappedNotebooks == notebookCovers {
        tappedNotebooks = []
        print("cleared Tapped notebooks")
    }
}


回答5:

It is possible with shuffle:

struct AnantShuffler<Base: MutableCollection> {

    private var collection: Base
    private var index: Base.Index

    public init?(collection: Base) {
        guard !collection.isEmpty else {
            return nil
        }
        self.collection = collection
        self.index = collection.startIndex
    }

    public mutating func next() -> Base.Iterator.Element {
        let result = collection[index]
        collection.formIndex(after: &index)
        if index == collection.endIndex {
            collection.shuffle()
            index = collection.startIndex
        }
        return result
    }
}

fileprivate extension MutableCollection {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }
}

Use:

let shuffler = AnantShuffler(collection: ["c1","c2","c3"].shuffled())
shuffler?.next()


标签: ios arrays swift