简体   繁体   中英

How to avoid generating the same random String twice in a row in Swift?

I'm trying to get a random value from an array of strings from an SQLite database, without getting the same value twice in a row (same value should be able to appear again later, but not immediately after). How can I achieve this in Swift?

It depends on your criteria for when the same value able to appear again

you can try using the shuffle or shuffled function to shuffle your array and you can get the value from first to last to make it never show the same value

let x = [1, 2, 3].shuffled()
// x == [2, 3, 1]

var numbers = [1, 2, 3, 4]
numbers.shuffle()
// numbers == [3, 2, 1, 4]

You can create a randomIndex method to return a random index except a previous index that you can store in a var. So the first time you pass nil it will return any index of a collection. If you pass the last random index just remove it from the collection indices, return a new random index and use it to call your method the next time you need a random index:

Something like:

extension RandomAccessCollection where Index == Int {
    func randomIndex(except index: Index? = nil) -> Index? {
        guard let index = index else { return indices.randomElement() }
        var indices = Array(self.indices)
        indices.remove(at: index)
        return indices.randomElement()
    }
}

Usage:

class ViewController: UIViewController {
    let strings = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
    lazy var index = strings.randomIndex()! // this will return any index of the collection to start
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    var randomElement: String {
        // this will get the next random index after returning the random element
        defer { index = strings.randomIndex(except: index)! }
        return strings[index]
    }
}

Playground testing:

let vc = ViewController()
vc.randomElement  // "Two"
vc.randomElement  // "Three"
vc.randomElement  // "Two"
vc.randomElement  // "Eight"
vc.randomElement  // "Nine"
vc.randomElement  // "Six"
vc.randomElement  // "One"
vc.randomElement  // "Four"
vc.randomElement  // "Six"
vc.randomElement  // "Five"
vc.randomElement  // "Eight"
vc.randomElement  // "Five"
vc.randomElement  // "Four"
vc.randomElement  // "Seven"
vc.randomElement  // "Four"

Since you only care about not repeating the value as the next value, just remember the previous value and reject the next value if it is the same as the previous value.

Here's a playground example. First, we try this the simplest way:

let pep = ["manny", "moe", "jack"]

func fetchRandomPep() -> String {
    return pep.randomElement()!
}

for _ in 1...100 {
    let result = fetchRandomPep()
    print(result)
}

Well, you probably got many repeated results. Let's prevent that by writing our loop in a different way:

var prev = ""
for _ in 1...100 {
    var result = fetchRandomPep()
    while result == prev {
        result = fetchRandomPep()
    }
    prev = result
    print(result)
}

Now there are no repetitions!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM