Swift: Index Out of Range Error when Populating Two-Dimensional Array - arrays

I just started to learn Swift and I would like to code the game BubbleBreaker which I have already created in both Java and C# some years ago.
For this, I wanted to create a two-dimensional array of Bubble (which is derived from SKSpriteNode), however, when I am trying to populate the array, I always get an "index out of range error" at index [0][0]. Can somebody please help me?
class GameScene: SKScene {
//game settings
private var columns = 10
private var rows = 16
private var bubbleWidth = 0
private var bubbleHeight = 0
//bubble array
private var bubbles = [[Bubble]]()
override func didMove(to view: SKView) {
initializeGame()
}
private func initializeGame() {
self.anchorPoint = CGPoint(x: 0, y: 0)
//Optimize bubble size
bubbleWidth = Int(self.frame.width) / columns
bubbleHeight = Int(self.frame.height) / rows
if bubbleWidth < bubbleHeight {
bubbleHeight = bubbleWidth
}
else {
bubbleWidth = bubbleHeight
}
//Initialize bubble array
for i in 0 ... columns-1 {
for j in 0 ... rows-1 {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
bubbles[i][j] = newBubble // THIS IS WERE THE CODE BREAKS AT INDEX [0][0]
self.addChild(newBubble)
}
}
}
}

bubbles starts off empty. There's nothing at any index.
Update your loop to something like this:
//Initialize bubble array
for i in 0 ..< columns {
var innerArray = [Bubble]()
for j in 0 ..< rows {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
innertArray.append(newBubble)
self.addChild(newBubble)
}
bubbles.append(innerArray)
}
This builds up an array of arrays.

Instead of assigning new value as unexisting value, append new empty array of Bubble for every column and then append to this array newBubble for every row
for i in 0 ... columns-1 {
bubbles.append([Bubble]())
for j in 0 ... rows-1 {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
bubbles[i].append(newBubble)
self.addChild(newBubble)
}
}

Related

Increment value in an array of dictionaries

I'm trying to calculate the number of times a specific number is rolled in a set of six six-sided dice, to determine wether or not I have three of a kind, four of a kind, etc.
I can pull the face value of each die rolled and compare it to the faces on a 6 sided die but can't get the "qtyRolled" key/value to increment.
func rollDice() {
currentRoll.removeAll()
for _ in currentDiceArray {
let num: UInt32 = arc4random_uniform(UInt32(currentDieFaceArray.count))
let currentDieData = currentDieFaceArray[Int(num)]
let faceValue = currentDieData["faceValue"]
currentRoll.append(faceValue as! Int)
}
print(currentRoll)
getQtyOfDieFaces()
//checkForScoringCombos()
}
func getQtyOfDieFaces() {
for die in currentRoll {
for dieData in currentDieFaceArray {
var currentDieData = dieData
let qtyRolled = currentDieData["qtyRolled"] as! Int
let faceValue = currentDieData["faceValue"] as! Int
print("faceValue: \(faceValue)")
print("Die: \(die)")
if faceValue == die {
currentDieData["qtyRolled"] = qtyRolled + 1 as AnyObject
}
}
}
for currentDieData in currentDieFaceArray {
print(currentDieData["qtyRolled"]!)
}
}
Here are my data structures
var currentDieFaceArray = [[String:AnyObject]]()
var currentDiceArray:[[String:AnyObject]] = [[:]]
var currentRoll: [Int] = []
I'd recommend ditching the dictionaries unless you really need them, as you're really just dealing with properties of a struct/class. I'm going to assume you're using the currentDieFaceArray method so that you can make this generic for non-linear dice faces of other dimensions (e.g. you can have a four-sided dice with the face values [1, 4, 6, 8]). If this isn't the case, you can simplify further I'm sure with a simple array of counts. But here's an example with your method (probably has other possible optimisations).
class DieFaceDefn
{
let faceValue : Int
var countThisRoll : Int = 0
init(faceValue: Int)
{
self.faceValue = faceValue
}
}
var diceFaces: [DieFaceDefn] = []
let numberOfCurrentDice = 5
func setupDice()
{
diceFaces.append(DieFaceDefn(faceValue: 1))
diceFaces.append(DieFaceDefn(faceValue: 2))
...
}
var currentRoll: [Int] = []
func rollDice()
{
currentRoll.removeAll()
diceFaces.forEach { $0.countThisRoll = 0 }
for _ in 0..<numberOfCurrentDice
{
let num: UInt32 = arc4random_uniform(UInt32(diceFaces.count))
let currentDieData = diceFaces[Int(num)]
let faceValue = currentDieData.faceValue
currentRoll.append(faceValue)
currentDieData.countThisRoll += 1
}
print(currentRoll)
diceFaces.forEach { print("\($0.faceValue): \($0.countThisRoll)") }
}

Swift 4 SprikeKit - Thread 1: Fatal error: Index out of range

I am just learning Swift and I am following along with another project that I had worked on. However, I am getting this error:
Error I am getting:
Thread 1: Fatal error: Index out of range
All my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var scoreLabel : SKLabelNode?
var player : SKSpriteNode?
var track : Int = 0
var trackArray : [SKSpriteNode]? = [SKSpriteNode]()
var ballDirection = Int(arc4random_uniform(2))
var velocityArray = [Int]()
var playerVelocity : Int = GKRandomSource.sharedRandom().nextInt(upperBound: 100)
var currentScore : Int = 0 {
didSet {
self.scoreLabel?.text = "SCORE: \(self.currentScore)"
}
}
func createHUD() {
scoreLabel = self.childNode(withName: "score") as? SKLabelNode
currentScore = 0
}
//I think the problem is coming from this function, however I more-or-less copied it from a tutorial project that works so I don't know what the problem is.
func setupTracks() {
for i in {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
trackArray?.append(track)
print(track)
}
}
}
func createBall() {
player = SKSpriteNode(imageNamed: "ball1")
player?.physicsBody = SKPhysicsBody(circleOfRadius: player!.size.width / 2)
player?.physicsBody?.linearDamping = 0
//This line throws the error
let ballPosition = trackArray?[track].position
print(track)
player?.position = CGPoint(x: (ballPosition?.x)!, y: (ballPosition?.y)!)
player?.position.x = (ballPosition!.x)
player?.position.y = (ballPosition!.y)
self.addChild(player!)
}
override func didMove(to view: SKView) {
createHUD()
createBall()
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I have 8 Color Sprite objects that I put in through the GameScene.sks each named 0 - 9,
I think it is looking for a value in the array that has not been yet assigned? but I have a part that (I think) is assigning that value. It also works in the other project so I am very lost.
Some of this doesn't make 100% sense to me without context, but based on what I can infer it seems a better solution for you would be to use a single dictionary to access the SKSpriteNode as well as the direction and velocity. To achieve this you could create a custom object, or just use a simple tuple.
After you initialize the dictionary and fill it with the objects you can access a single value in the dictionary to get all of the data you need for your createEnemy function. It might look something like this:
var tracks = [Int : (node: SKSpriteNode, direction: CGFloat, velocity: CGFloat)]()
func setupTrack() {
for i in 0 ... 9 {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
//Get direction and velocity here, if initial value is known
//Setting to 0 for now
tracks[i] = (node: track, direction: 0, velocity: 0)
}
}
}
func createEnemy(forTrack track: Int) -> SKShapeNode? {
guard let thisTrack = tracks[track] else { return nil }
let enemySprite = SKShapeNode()
enemySprite.name = "ENEMY"
enemySprite.path = CGPath(roundedRect: CGRect(x: 0, y: -10, width: 20, height: 20), cornerWidth: 10, cornerHeight: 10, transform: nil)
let enemyPosition = thisTrack.node.position
let left = thisTrack.direction
enemySprite.position.x = left ? -50 : self.size.height + 50
enemySprite.position.y = enemyPosition.y
enemySprite.physicsBody?.velocity = left ? CGVector(dx: thisTrack.velocity, dy: 0) : CGVector(dx: 0, dy: -thisTrack.velocity)
return enemySprite
}
This will prevent you from going out of bounds on your array. Note on this: Since you are looping through a hardcoded 0...9 range, but you are optionally appending items to that Array (in the case where your if let track fails) you have no guarantee that the "track" item with the name "7" corresponds to the 7th index in your array. This is another reason a Dictionary is safer. Additionally, it seems you are maintaining 3 separate arrays (tracks, directionArray, and velocityArray), which seems prone to error for the same reason I just described.

Addressing a multi-dimensional array of objects in Swift

Extreme newbie Swift syntax question...
Trying to address a multidimensional array of UIImageView objects and getting the error
Cannot subscript a value of type [[UIImageView]] with an index of
type UInt32.
I thought I was creating an NxN array of UIImageView objects but I guess not?
Thanks All!
func loadDefaultImages()
{
var pictures : [[UIImageView]] = [];
let MaxRows: UInt32 = 4
let MaxCols: UInt32 = 4
var row: UInt32
var col: UInt32
for (row = 0; row <= MaxRows; row++)
{
for (col = 0; col <= MaxCols; col++)
{
var newImage = UIImageView(image: UIImage(named: "first"))
pictures[row][col] = UIImageView() /* Error on this statement */
}
}
}
Initially, your pictures array will not contain any elements, (i.e. it will evaluate to []). As you try to set pictures[0][0], you're trying to reference both a row and column that do not exist yet.
Instead, you could build up the array as you go, like this:
func loadDefaultImages() {
var pictures : [[UIImageView]] = [];
let MaxRows: UInt32 = 4
let MaxCols: UInt32 = 4
for _ in 0..<MaxRows {
var aRow : [UIImageView] = []
for _ in 0..<MaxCols {
let newImage = UIImageView(image: UIImage(named: "first"))
aRow.append(newImage)
}
pictures.append(aRow)
}
}
However, a neater way to do this would be to create the array with a default size using the count, repeatedValue function, like so:
func loadDefaultImages() {
let MaxRows: Int = 4
let MaxCols: Int = 4
var pictures = [[UIImageView?]](count: MaxRows, repeatedValue: [UIImageView?](count: MaxCols, repeatedValue: nil))
for row in 0..<MaxRows {
for col in 0..<MaxCols {
let newImage = UIImageView(image: UIImage(named: "first"))
pictures[row][col] = newImage
}
}
}

extracting csv data and plotting 2D array using scala and processing

I was wondering if anybody might be able to shed some light on this problem, I'm quite new to scala and generally non R programming, so i'm still learning some of the ropes so to speak.
What i'm trying to do is plot a 2D array, so a grid of squares on a gray scale with a darker colour representing a higher value. I have a csv file of x and y locations on the grid and a third value z which will be represented by the colour.
I've written two pieces of code which do what I want, one extracts data from a csv file, the other plots the type of data that is extracted. The problem I have is getting them to work together.
So piece of code 1 extracts data which is as follows:
x, y, z
50, 16, 52
21, 25, 29
13, 12, 445
etc...
code:
import scala.io.Source
import java.io._
///////extract the data
object DataExtractor extends App {
def using[T <: Closeable, R](resource: T)(block: T => R): R = {
try { block(resource) }
finally { resource.close() }
}
for (line <- io.Source.fromFile("C://~//TestData.csv").getLines.drop(1)) {
val array = line.split(",")
try {
val xloc = array(0)
val yloc = array(1)
val intense = array (2)
}catch { case ex: Exception => }
}
}
This gives me a value from each line of the csv the x axis location (xloc), y axis (yloc) and what will be the intensity value (intense) in order to plot how darkly shaded the grid square is.
The second piece of code plots a 2D array using "processing"
/////////plotting data
import processing.core._
class FT extends processing.core.PApplet {
val xloc = 9 //define locations
val yloc = 1
val intense = 4 //define intensity of colour
var grid: Array[Array[Cell]] = _
var cols: Int = 100 // grid size
var rows: Int = 100
override def setup() {
size(200, 200) //window size
grid = Array.ofDim[Cell](cols, rows)
for (i <- 0 until cols; j <- 0 until rows) {
grid(i)(j) = new Cell(i * 10, j * 10, 10, 10, i + j) //cell size
}
}
override def draw() {
background(0)
for (i <- 0 until cols; j <- 0 until rows) {
grid(i)(j).display()
grid(i)(j).height()
grid(i)(j).xl()
grid(i)(j).yl()
}
}
class Cell(var x: Float,
var y: Float,
var w: Float,
var h: Float,
var strength: Float) {
def display() {
stroke(255)
fill(strength)
rect(x, y, w, h)
}
def height() {
strength = intense
}
def xl() {
x = xloc
}
def yl() {
y = yloc
}
}
}
As you can see, in my second piece of code i've given xloc, yloc and intense arbitrary values. What i'm trying to do is to loop through a csv file and for each line plug in the data to the plotting code so the final result is a grid with differing colour intensities based on the x, y and z values in the csv file.
Any help will be much appreciated as i'm still trying to figure out how one links chunks of code together in scala.
Edit:
So what i've tried is to integrate the two pieces of code, however I'm running into a new problem which is that scala will not initialize a local varaible within a method (var grid). But by moving grid outside of the loop it can no longer access the Cell class...any pointers or ideas would be much appreciated
import processing.core._
import scala.io.Source
import java.io._
class FT extends processing.core.PApplet {
for (line <- io.Source.fromFile("C:~TestData.csv").getLines.drop(1)) {
try {
val array = line.split(",")
var xloct = array(0)
var yloct = array(1)
var intenset = array (2)
val xloc = xloct.toFloat
val yloc = yloct.toFloat
val intense = intenset.toFloat
var grid: Array[Array[Cell]] =_ // error "local variables must be initialized"
var cols: Int = 100 // grid size
var rows: Int = 100
def setup() {
size(200, 200) //window size
grid = Array.ofDim[Cell](cols, rows)
for (i <- 0 until cols; j <- 0 until rows) {
grid(i)(j) = new Cell(i * 10, j * 10, 10, 10, i + j) //cell size
}
}
def draw() {
background(0)
for (i <- 0 until cols; j <- 0 until rows) {
grid(i)(j).display()
grid(i)(j).height()
grid(i)(j).xl()
grid(i)(j).yl()
}
}
class Cell(var x: Float,
var y: Float,
var w: Float,
var h: Float,
var strength: Float) {
def display() {
stroke(255)
fill(strength)
rect(x, y, w, h)
}
def height() {
strength = intense
}
def xl() {
x = xloc
}
def yl() {
y = yloc
}
}
} catch { case ex: Exception => }
}
}

How do I make this extension of Array? [duplicate]

Suppose I have an array and I want to pick one element at random.
What would be the simplest way to do this?
The obvious way would be array[random index]. But perhaps there is something like ruby's array.sample? Or if not could such a method be created by using an extension?
Swift 4.2 and above
The new recommended approach is a built-in method on the Collection protocol: randomElement(). It returns an optional to avoid the empty case I assumed against previously.
let array = ["Frodo", "Samwise", "Merry", "Pippin"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0
If you don't create the array and aren't guaranteed count > 0, you should do something like:
if let randomElement = array.randomElement() {
print(randomElement)
}
Swift 4.1 and below
Just to answer your question, you can do this to achieve random array selection:
let array = ["Frodo", "Samwise", "Merry", "Pippin"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])
The castings are ugly, but I believe they're required unless someone else has another way.
Riffing on what Lucas said, you could create an extension to the Array class like this:
extension Array {
func randomItem() -> Element? {
if isEmpty { return nil }
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
For example:
let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>
Swift 4 version:
extension Collection where Index == Int {
/**
Picks a random element of the collection.
- returns: A random element of the collection.
*/
func randomElement() -> Iterator.Element? {
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
}
}
In Swift 2.2 this can be generalised so that we have:
UInt.random
UInt8.random
UInt16.random
UInt32.random
UInt64.random
UIntMax.random
// closed intervals:
(-3...3).random
(Int.min...Int.max).random
// and collections, which return optionals since they can be empty:
(1..<4).sample
[1,2,3].sample
"abc".characters.sample
["a": 1, "b": 2, "c": 3].sample
First, implementing static random property for UnsignedIntegerTypes:
import Darwin
func sizeof <T> (_: () -> T) -> Int { // sizeof return type without calling
return sizeof(T.self)
}
let ARC4Foot: Int = sizeof(arc4random)
extension UnsignedIntegerType {
static var max: Self { // sadly `max` is not required by the protocol
return ~0
}
static var random: Self {
let foot = sizeof(Self)
guard foot > ARC4Foot else {
return numericCast(arc4random() & numericCast(max))
}
var r = UIntMax(arc4random())
for i in 1..<(foot / ARC4Foot) {
r |= UIntMax(arc4random()) << UIntMax(8 * ARC4Foot * i)
}
return numericCast(r)
}
}
Then, for ClosedIntervals with UnsignedIntegerType bounds:
extension ClosedInterval where Bound : UnsignedIntegerType {
var random: Bound {
guard start > 0 || end < Bound.max else { return Bound.random }
return start + (Bound.random % (end - start + 1))
}
}
Then (a little more involved), for ClosedIntervals with SignedIntegerType bounds (using helper methods described further below):
extension ClosedInterval where Bound : SignedIntegerType {
var random: Bound {
let foot = sizeof(Bound)
let distance = start.unsignedDistanceTo(end)
guard foot > 4 else { // optimisation: use UInt32.random if sufficient
let off: UInt32
if distance < numericCast(UInt32.max) {
off = UInt32.random % numericCast(distance + 1)
} else {
off = UInt32.random
}
return numericCast(start.toIntMax() + numericCast(off))
}
guard distance < UIntMax.max else {
return numericCast(IntMax(bitPattern: UIntMax.random))
}
let off = UIntMax.random % (distance + 1)
let x = (off + start.unsignedDistanceFromMin).plusMinIntMax
return numericCast(x)
}
}
... where unsignedDistanceTo, unsignedDistanceFromMin and plusMinIntMax helper methods can be implemented as follows:
extension SignedIntegerType {
func unsignedDistanceTo(other: Self) -> UIntMax {
let _self = self.toIntMax()
let other = other.toIntMax()
let (start, end) = _self < other ? (_self, other) : (other, _self)
if start == IntMax.min && end == IntMax.max {
return UIntMax.max
}
if start < 0 && end >= 0 {
let s = start == IntMax.min ? UIntMax(Int.max) + 1 : UIntMax(-start)
return s + UIntMax(end)
}
return UIntMax(end - start)
}
var unsignedDistanceFromMin: UIntMax {
return IntMax.min.unsignedDistanceTo(self.toIntMax())
}
}
extension UIntMax {
var plusMinIntMax: IntMax {
if self > UIntMax(IntMax.max) { return IntMax(self - UIntMax(IntMax.max) - 1) }
else { return IntMax.min + IntMax(self) }
}
}
Finally, for all collections where Index.Distance == Int:
extension CollectionType where Index.Distance == Int {
var sample: Generator.Element? {
if isEmpty { return nil }
let end = UInt(count) - 1
let add = (0...end).random
let idx = startIndex.advancedBy(Int(add))
return self[idx]
}
}
... which can be optimised a little for integer Ranges:
extension Range where Element : SignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}
extension Range where Element : UnsignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}
You can use Swift's built-in random() function as well for the extension:
extension Array {
func sample() -> Element {
let randomIndex = Int(rand()) % count
return self[randomIndex]
}
}
let array = [1, 2, 3, 4]
array.sample() // 2
array.sample() // 2
array.sample() // 3
array.sample() // 3
array.sample() // 1
array.sample() // 1
array.sample() // 3
array.sample() // 1
Another Swift 3 suggestion
private extension Array {
var randomElement: Element {
let index = Int(arc4random_uniform(UInt32(count)))
return self[index]
}
}
Following others answer but with Swift 2 support.
Swift 1.x
extension Array {
func sample() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
Swift 2.x
extension Array {
func sample() -> Element {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
E.g.:
let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()
An alternative functional implementation with check for empty array.
func randomArrayItem<T>(array: [T]) -> T? {
if array.isEmpty { return nil }
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
return array[randomIndex]
}
randomArrayItem([1,2,3])
Here's an extension on Arrays with an empty array check for more safety:
extension Array {
func sample() -> Element? {
if self.isEmpty { return nil }
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
return self[randomInt]
}
}
You can use it as simple as this:
let digits = Array(0...9)
digits.sample() // => 6
If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via Carthage then use it exactly like in the example above:
import HandySwift
let digits = Array(0...9)
digits.sample() // => 8
Additionally it also includes an option to get multiple random elements at once:
digits.sample(size: 3) // => [8, 0, 7]
Swift 3
import GameKit
func getRandomMessage() -> String {
let messages = ["one", "two", "three"]
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)
return messages[randomNumber].description
}
Swift 3 - simple easy to use.
Create Array
var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
Create Random Color
let randomColor = arc4random() % UInt32(arrayOfColors.count)
Set that color to your object
your item = arrayOfColors[Int(randomColor)]
Here is an example from a SpriteKit project updating a SKLabelNode with a random String:
let array = ["one","two","three","four","five"]
let randomNumber = arc4random() % UInt32(array.count)
let labelNode = SKLabelNode(text: array[Int(randomNumber)])
If you want to be able to get more than one random element out of your array with no duplicates, GameplayKit has you covered:
import GameplayKit
let array = ["one", "two", "three", "four"]
let shuffled = GKMersenneTwisterRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
let firstRandom = shuffled[0]
let secondRandom = shuffled[1]
You have a couple choices for randomness, see GKRandomSource:
The GKARC4RandomSource class uses an algorithm similar to that employed in arc4random family of C functions. (However, instances of this class are independent from calls to the arc4random functions.)
The GKLinearCongruentialRandomSource class uses an algorithm that is faster, but less random, than the GKARC4RandomSource class. (Specifically, the low bits of generated numbers repeat more often than the high bits.) Use this source when performance is more important than robust unpredictability.
The GKMersenneTwisterRandomSource class uses an algorithm that is slower, but more random, than the GKARC4RandomSource class. Use this source when it’s important that your use of random numbers not show repeating patterns and performance is of less concern.
I find using GameKit's GKRandomSource.sharedRandom() works best for me.
import GameKit
let array = ["random1", "random2", "random3"]
func getRandomIndex() -> Int {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
return randomNumber
or you could return the object at the random index selected. Make sure the function returns a String first, and then return the index of the array.
return array[randomNumber]
Short and to the point.
There is a built-in method on Collection now:
let foods = ["πŸ•", "πŸ”", "🍣", "🍝"]
let myDinner = foods.randomElement()
If you want to extract up to n random elements from a collection you can add an extension like this one:
extension Collection {
func randomElements(_ count: Int) -> [Element] {
var shuffledIterator = shuffled().makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
And if you want them to be unique you can use a Set, but the elements of the collection must conform to the Hashable protocol:
extension Collection where Element: Hashable {
func randomUniqueElements(_ count: Int) -> [Element] {
var shuffledIterator = Set(shuffled()).makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
Latest swift3 code try it its working fine
let imagesArray = ["image1.png","image2.png","image3.png","image4.png"]
var randomNum: UInt32 = 0
randomNum = arc4random_uniform(UInt32(imagesArray.count))
wheelBackgroundImageView.image = UIImage(named: imagesArray[Int(randomNum)])
I figured out a very different way to do so using the new features introduced in Swift 4.2.
// πŸ‘‡πŸΌ - 1
public func shufflePrintArray(ArrayOfStrings: [String]) -> String {
// - 2
let strings = ArrayOfStrings
//- 3
var stringans = strings.shuffled()
// - 4
var countS = Int.random(in: 0..<strings.count)
// - 5
return stringans[countS]
}
we declared a function with parameters taking an array of Strings and returning a String.
Then we take the ArrayOfStrings in a variable.
Then we call the shuffled function and store that in a variable. (Only supported in 4.2)
Then we declare a variable which saves a shuffled value of total count of the String.
Lastly we return the shuffled string at the index value of countS.
It is basically shuffling the array of strings and then also have a random pick of number of the total number of count and then returning the random index of the shuffled array.

Resources