I am using arc4random to generate 10 random numbers so I can then query firebase to get the questions that contain the randomly generated numbers. The problem is that I don't want any number to appear more than once so then there are not duplicate questions. Current code below...
import UIKit
import Firebase
class QuestionViewController: UIViewController {
var amountOfQuestions: UInt32 = 40
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Use a for loop to get 10 questions
for _ in 1...10{
// generate a random number between 1 and the amount of questions you have
let randomNumber = Int(arc4random_uniform(amountOfQuestions - 1)) + 1
print(randomNumber)
// The reference to your questions in firebase (this is an example from firebase itself)
let ref = Firebase(url: "https://test.firebaseio.com/questions")
// Order the questions on their value and get the one that has the random value
ref.queryOrderedByChild("value").queryEqualToValue(randomNumber)
.observeEventType(.ChildAdded, withBlock: {
snapshot in
// Do something with the question
print(snapshot.key)
})
}
}
#IBAction func truepressed(sender: AnyObject) {
}
#IBAction func falsePressed(sender: AnyObject) {
}
}
You can have an array to store the value you want to random with, in your case, [1,2,3....10], and then use arc4random to get the random index of any value inside (0..9), get the value and remove it from array. Then you will never get the same number from the array.
Given the total number of questions
let questionsCount = 100
you can generate a sequence of integers
var naturals = [Int](0..<questionsCount)
Now given the quantity of unique random numbers you need
let randomsCount = 10
that of course should not exceed the total number of questions
assert(randomsCount <= questionsCount)
you can build your list of unique integers
let uniqueRandoms = (1..<randomsCount).map { _ -> Int in
let index = Int(arc4random_uniform(UInt32(naturals.count)))
return naturals.removeAtIndex(index)
}
As an alternative to generating the random number on the client and requesting a question at that specified number, you could download the entire array of questions and shuffle the array. GameKit provides a built-in method to shuffle the array.
import GameKit
// ...
let ref = Firebase(url: "https://test.firebaseio.com/questions")
// Order the questions on their value and get the one that has the random value
ref.queryOrderedByChild("value")
.observeEventType(.ChildAdded, withBlock: {
snapshot in
// Shuffle your array
let shuffledQuestions = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(snapshot)
// Store your array somewhere and iterate through it for the duration of your game
})
You could generate random numbers and store each number in an NSArray. However, when you append it to the array you can check if the array already contains that number.
For example:
for _ in 1...10 {
let amountOfQuestions: UInt32 = 40
var intArray: [Int] = []
let randomNumber = Int(arc4random_uniform(amountOfQuestions - 1)) + 1
if intArray.contains(randomNumber) {
repeat {
randomNumber = Int(arc4random_uniform(amountOfQuestions - 1)) + 1
} while intArray.contains(randomNumber)
if !intArray.contains(randomNumber) {
intArray.append(randomNumber)
}
} else {
intArray.append(randomNumber)
}
print(intArray)
}
After that you can make a FireBase request with your uniquely generated integers. I hope I was able to help :).
Create unsorted Array of ten random Int 0..99
var set = Set<Int>()
while set.count < 10 {
let num = Int(arc4random_uniform(100))
set.insert(num)
}
let arr = Array(set)
Related
Here I am just getting array count and using for loop but got crashed at let arr = arrayAdaptor[i] this line after completing my array count don't know why it's crashing can anyone help me how to resolve this
var arrayAdaptor = [Struct_Row_Rating]()
for i in 0...arrayAdaptor.count {
let arr = arrayAdaptor[i]
let number = arr.row
let row = number + 1
dict.updateValue("\(arr.rating)", forKey: "\(row)")
print(dict)
}
struct Struct_Row_Rating {
var row: Int
var rating: Double
init(row: Int , rating: Double) {
self.row = row
self.rating = rating
}}
The operator ... exceeds the range of the array. You have to write
for i in 0..<arrayAdaptor.count
or
for i in 0...arrayAdaptor.count - 1
Basically don't use these index based for loops in Swift at all.
Use Fast Enumeration:
for arr in arrayAdaptor {
and if you really need the index
for (index, arr) in arrayAdaptor.enumerated() {
Why you are using 0...arrayAdaptor.count range style avoid it and Simply Use enumerated() of an Array:
for (_,value) in arrayAdaptor.enumerated() {
let number = value.row
let row = number + 1
dict.updateValue("\(arr.rating)", forKey: "\(row)")
print(dict)
}
If you don't want any index to try with this:
for value in arrayAdaptor {
let number = value.row
let row = number + 1
dict.updateValue("\(arr.rating)", forKey: "\(row)")
print(dict)
}
See this
One of the options not mentioned in the answers is forEach approach.
arrayAdaptor.forEach { item in
print(item)
}
or
arrayAdaptor.forEach {
print($0) // without named parameter
}
// Your case:
arrayAdaptor.forEach { item in
let arr = item
let number = item.row
let row = number + 1
dict.updateValue("\(arr.rating)", forKey: "\(row)")
print(dict)
}
This is pretty much the same as the handy for..in mentioned in vadian's answer:
for arr in arrayAdaptor { ... }
From Swift 4 you can use One sided ranges.
i... is favored over i..< because the latter is ugly. We have to pick one, two would be redundant and likely to cause confusion over which is the "right" one. Either would be reasonable on pedantic correctness grounds – (i as Int)... includes Int.max consistent with ..., whereas a[i...] is interpreted as a[i..<a.endIndex] consistent with i..<.
Example:
var names = ["Jack", "New", "peter"]
let first = names[0...]
let second = names[..<names.count]
print(first)//prints ["Jack", "New", "peter"]
print(second)//prints ["Jack", "New", "peter"]
You can also iterate loop by 'map' function:
let _ = arrayAdaptor.map({ adaptor in
let number = adaptor.row
let row = number + 1
dict.updateValue("\(adaptor.rating)", forKey: "\(row)")
print(dict)
})
I have an array of strings. I would like to display 3 unique items from this array randomly. Then every 5 seconds, one of the items gets replaced with another unique item (my idea here is adding an animation with a delay).
I can display the 3 strings, however sometimes they repeat, and the timer is not updating the factLabel label.
Here's my progress:
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func randomFact() -> String {
let arrayCount = model.cancunFacts.count
let randomIndex = Int(arc4random_uniform(UInt32(arrayCount)))
return model.cancunFacts[randomIndex]
}
// Display the facts
func updateUI() {
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(randomFact), userInfo: nil, repeats: true)
factLabel.text = randomFact() + " " + randomFact() + " " + randomFact()
}
How do I get the text to always update randomly, without the 3 facts repeating?
Create an array of indexes. Remove a random index from the array, use it to index into your strings. When the array of indexes is empty, refill it.
Here is some sample code that will generate random, non-repeating strings:
var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief",
"F***face Von Clownstick", "Short-Fingered Vulgarian",
"Drumpf", "Der Gropenführer", "Pumpkin in a suit"]
var indexes = [Int]()
func randomString() -> String {
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
}
let index = Int(arc4random_uniform(UInt32(indexes.count)))
let randomIndex = indexes.remove(at: index)
return randomStrings[randomIndex]
}
for i in 1...100 {
print (randomString())
}
(Note that it may still generate repeating strings in the case when the array of indexes is empty and it needs to be refilled. You'd need to add extra logic to prevent that case.)
Version 2:
Here is the same code, modified slightly to avoid repeats when the array of indexes is empty and needs to be refilled:
var randomStrings = ["tiny-fingered", "cheeto-faced", "ferret-wearing", "sh*tgibbon"]
var indexes = [Int]()
var lastIndex: Int?
func randomString() -> String {
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
}
var randomIndex: Int
repeat {
let index = Int(arc4random_uniform(UInt32(indexes.count)))
randomIndex = indexes.remove(at: index)
} while randomIndex == lastIndex
lastIndex = randomIndex
return randomStrings[randomIndex]
}
for i in 1...10000 {
print (randomString())
}
Even though it's using a repeat...while statement, the repeat condition will never fire twice in a row, because you'll never get a repeat except right after refilling the array of indexes.
With that code, if there is a repeat, the selected string will be skipped on that pass through the array. To avoid that, you'd need to adjust the code slightly to not remove a given index from the array until you verify that it is not a repeat.
Version 3:
Version 2, above, will skip an entry if it picks a repeat when it refills the array. I wrote a 3rd version of the code that refills the array, removes the last item it returned so that it can't repeat, and then adds it back to the array once it's picked a random item. This third version will always return every item in the source array before refilling it and will also never repeat an item. Thus it's truly random with no bias:
import UIKit
var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief",
"F***face Von Clownstick", "Short-Fingered Vulgarian",
"Drumpf", "Der Gropenführer", "Pumpkin in a suit"]
var indexes = [Int]()
var lastIndex: Int?
var indexToPutBack: Int?
func randomString() -> String {
//If our array of indexes is empty, fill it.
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
print("") //Print a blank line each time we refill the array so you can see
//If we have returned an item previously, find and remove that index
//From the refilled array
if let lastIndex = lastIndex,
let indexToRemove = indexes.index(of: lastIndex) {
indexes.remove(at: indexToRemove)
indexToPutBack = indexToRemove //Remember the index we removed so we can put it back.
}
}
var randomIndex: Int
let index = Int(arc4random_uniform(UInt32(indexes.count)))
randomIndex = indexes.remove(at: index)
//If we refilled the array and removed an index to avoid repeats, put the removed index back in the array
if indexToPutBack != nil{
indexes.append(indexToPutBack!)
indexToPutBack = nil
}
lastIndex = randomIndex
return randomStrings[randomIndex]
}
for i in 1...30 {
print (randomString())
}
Sample output:
Short-Fingered Vulgarian
F***face Von Clownstick
Pumpkin in a suit
Drumpf
Lord Dampnut
Traitor
Der Gropenführer
Cheeto-In-Chief
Der Gropenführer
Drumpf
Lord Dampnut
Short-Fingered Vulgarian
Cheeto-In-Chief
Pumpkin in a suit
Traitor
F***face Von Clownstick
Short-Fingered Vulgarian
F***face Von Clownstick
Drumpf
Traitor
Cheeto-In-Chief
Lord Dampnut
Pumpkin in a suit
Der Gropenführer
Lord Dampnut
Short-Fingered Vulgarian
Pumpkin in a suit
Cheeto-In-Chief
Der Gropenführer
F***face Von Clownstick
Your timer is calling random fact, which simply returns a fact and doesn't do anything. You should probably have some third method called initializeTimer that does the Timer.scheduledtimer, which you should take out of your updateUI method. That timer should call updateUI. This would fix your labels updating. Also you would call initializeTimer in your viewDidLoad instead of updateUI. As far as preventing the repetition of facts, Duncan C's idea of having a separate array that you remove items from as you set new random facts then fill back up when it's empty seems like a good idea.
It may be easiest to maintain two arrays, usedStrings and unusedStrings, of random strings, like this:
var unusedStrings: [String] = ["king", "philip", "calls", "out", "for", "good", "soup"]
var usedStrings: [String] = []
func randomString() -> String {
if unusedStrings.isEmpty() {
unusedStrings = usedStrings
usedStrings = []
}
let randomIndex = Int(arc4random_uniform(unusedStrings.count))
let randomString = unusedStrings[randomIndex]
usedStrings.append(randomString)
return randomString
}
New to programming and trying to make my first game. It's 4 UIImages that I want to "turn on" in a pattern as the user copies the pattern. From there is follows that same pattern and increases the random pattern. Is arc4random.uniform() what I am looking for here? I know that generates a random number but I'm stuck on how to get the random image to repeat and add +1 random to it.
So you have 4 images, I'm guessing you want a random number between 1 and 4 to represent the images you want to "turn on". The following can generate a random sequence of images for you (you can test this out in a Swift Playground):
import Foundation
let numberOfImages = 4
func addRandom(array: [Int]) -> [Int] {
var returnArray = array
returnArray.append(Int(arc4random()) % numberOfImages)
return returnArray
}
func makePattern(length: Int) -> [Int] {
var pattern = [Int]()
for _ in 0...(length - 1) {
pattern = addRandom(pattern)
}
return pattern
}
// Array of 3 random digits
var firstPattern = makePattern(3)
// Same array as before, with one more random digit at the end
var nextPattern = addRandom(firstPattern)
// Same array as before, with yet another random digit at the end
var lastPattern = addRandom(nextPattern)
Just for fun, you can do this even more cleanly with a Pattern class, like so:
import Foundation
class Pattern {
private(set) var maxRandomNumber: Int!
private(set) var pattern: [Int] = [Int]()
init(maxRandomNumber: Int) {
self.maxRandomNumber = maxRandomNumber
}
init(length: Int, maxRandomNumber: Int) {
self.maxRandomNumber = maxRandomNumber
for _ in 0...(length - 1) {
self.appendRandom()
}
}
func appendRandom() {
self.pattern.append(Int(arc4random()) % self.maxRandomNumber)
}
}
// Initialize a new pattern with 3 iterations and a max random number of 4
var pattern = Pattern(length: 3, maxRandomNumber: 4)
// See the pattern
print(pattern.pattern)
// Add another random number on to the end and check
pattern.appendRandom()
print(pattern.pattern)
Like: 0123, 0913, 7612
Not like: 0000, 1333, 3499
Can it be done with arcRandom() in swift? Without array or loop?
Or If that impossible, how it be done with arcRandom() in any way ?
You just want to shuffle the digits and pick the number you want.
Start with Nate Cook's Fischer-Yates shuffle code.
// Start with the digits
let digits = 0...9
// Shuffle them
let shuffledDigits = digits.shuffle()
// Take the number of digits you would like
let fourDigits = shuffledDigits.prefix(4)
// Add them up with place values
let value = fourDigits.reduce(0) {
$0*10 + $1
}
var fourUniqueDigits: String {
var result = ""
repeat {
// create a string with up to 4 leading zeros with a random number 0...9999
result = String(format:"%04d", arc4random_uniform(10000) )
// generate another random number if the set of characters count is less than four
} while Set<Character>(result.characters).count < 4
return result // ran 5 times
}
fourUniqueDigits // "3501"
fourUniqueDigits // "8095"
fourUniqueDigits // "9054"
fourUniqueDigits // "4728"
fourUniqueDigits // "0856"
Swift Code - For Generation of 4 digit
It gives number between 1000 and 9999.
func random() -> String {
var result = ""
repeat {
result = String(format:"%04d", arc4random_uniform(10000) )
} while result.count < 4 || Int(result)! < 1000
print(result)
return result
}
Please Note - You can remove this Int(result)! < 1000 if you want numbers like this - 0123, 0913
So, I am using the official Hacker News API and it is hosted on Firebase.
The problem I am having is that I want to get a subset of a list basically.
Something along the way of.
let topNewsRef = firebase.childByAppendingPath("topstories").queryLimitedToFirst(UInt(batchSize + offset)).queryLimitedToLast(UInt(batchSize))
[I know this does not work but I would like this effect]. Basically I want a subset of a set, specified by a range; from item 2 to item 15, for example].
Say I want 50 items from the 75th item, but the above is not working. So the question is; how do I achieve the same effect?
For example; given a list of 100 items in Firebase. I want all item from the 50th and 75th. There is no property that gives away the order of the items.
Here is my current solution;
let topNewsRef = firebase.childByAppendingPath("topstories").queryLimitedToFirst(UInt(batchSize + offset))
var handle: UInt?
handle = topNewsRef.observeEventType(.Value) { (snapshot: FDataSnapshot!) -> Void in
if let itemIDs = snapshot.value as? [Int] {
itemIDs.dropFirst(offset) // This drops all items id I already fetched ...
for itemID in itemIDs {
let itemRef = self.firebase.childByAppendingPath("item/\(itemID)")
var itemHandle: UInt?
itemHandle = itemRef.observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) -> Void in
if let itemHandle = itemHandle {
itemRef.removeObserverWithHandle(itemHandle)
}
if let json = snapshot.value as? [String:AnyObject],
// Handle JSON ...
}
})
}
}
if let handle = handle {
topNewsRef.removeObserverWithHandle(handle)
}
} // offset += batchSize
... which is gettting all items from the start (offset) to the end (batchSize + offset), then I drop the first end of the list by the size of offset. Thus leaving on the list with a size of batchSize left.
Following our comments to your question,
You can use .queryOrderedByKey in combination with queryStartingAtValue:, queryEndingAtValue:
The data in /topstories is stored as an array. when ordering by key, the indices of the objects in the array are the keys. Then, all you need to do is cast the offset and batchSize as strings and pass them to queryStartingAtValue: & queryEndingAtValue: like so:
ref.queryOrderedByKey().queryStartingAtValue(String(offset)).queryEndingAtValue(String(offset+batchSize-1));
Don't forget to subtract 1 for the endingValue if you are incrementing offset by batchsize.
For the nth query in the series,
startingAtIndex = initialOffset + (batchSize * (n - 1))
endingAtIndex = initialOffset + (batchSize * n) - 1
For example, let batchSize=100 and the initial offset=0.
The first 100 items are keys 0 through 99. The key (index) of the first item in the array is 0.
If you query for endingAt(offset+batchSize), you will be ending at index 100, which is the 101st item in the array.
Thus, you will be getting 101 items -- one more than your batch size.
the second query will then be for items with indices 100-199 (starting at 0+batchsize and ending at 0+batchsize+batchsize-1), which is 100 items.
Here is an example PLNKR of the same concept implemented in JavaScript.
:)
Okay, so the problem was solved with the following query.
firebase.childByAppendingPath("topstories").queryOrderedByKey().queryStartingAtValue(String(offset)).queryEndingAtValue(String(offset + batchSize - 1))
The thing is that the Firebase documentation does not specify that the "AnyObject!" parameters can be used as indices on the query. They also forget to mention that the indices should be of type String.