I have an app with 2 image views. each one randomly displays an image of a dice face (from 1 to 6), the end results looks like the simulation of a dice toss for a player and for the computer.
it looks like this :
I have an array that contains the names for each image that is displayed :
let des: Array = ["one", "two", "three", "four", "five", "six"]
I also have a function that randomizes the image that is displayed for the 2 players
UIView.animate(withDuration: 0.5) {
self.dicePlayer.image = UIImage(named: self.des.randomElement() ?? "one")
self.diceComputer.image = UIImage(named: self.des.randomElement() ?? "two")
}
// on appelle la fonction qui gere les scores
gestionDesScores()
}
I would like to make comparison between those 2 tosses (i.e : if player one dice toss is superior to player 2 toss, then the player wins and vice versa).
Have your dice values in an enum:
enum Die: String,CaseIterable{
case One = "one"
case Two = "two"
case Three = "three"
case Four = "four"
case Five = "five"
case Six = "six"
}
Add an extension :
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.firstIndex { self == $0 }
}
}
And your throwDie method can look like :
func throwDice(){
let userResult = Die.allCases.randomElement()
let computerResult = Die.allCases.randomElement()
UIView.animate(withDuration: 0.2, animations: {
self.dicePlayer.image = UIImage(named: userResult?.rawValue ?? "one")
self.diceComputer.image = UIImage(named: computerResult?.rawValue ?? "one")
}) { (_) in
print("Index Value : \(computerResult?.index) : \(computerResult?.rawValue)")
//You may use the indexValue for comparison and the rawValue to assign your image
}
}
Self explanatory, I believe.
You can try
let fImg = self.des.randomElement() ?? "one"
let sImg = self.des.randomElement() ?? "two"
UIView.animate(withDuration: 0.5) {
self.dicePlayer.image = UIImage(named:fImg)
self.diceComputer.image = UIImage(named:sImg)
}
Then
if let fIndex = des.index(of:fImg) ,
let sIndex = des.index(of:sImg) ,
fIndex == (sIndex - 1) {
// wins
}
I would recommend storing the two tosses as human readable Int values. Add these properties to your class:
var playerToss = 1
var computerToss = 1
Then modify your function like this:
func lancerDeDes() {
self.playerToss = Int.random(in: 1...6)
self.computerToss = Int.random(in: 1...6)
UIView.animate(withDuration: 0.5) {
// subtract 1 because des array is indexed 0...5 not 1...6
self.dicePlayer.image = UIImage(named: self.des[self.playerToss - 1])
self.diceComputer.image = UIImage(named: self.des[self.computerToss - 1])
}
// on appelle la fonction qui gere les scores
gestionDesScores()
}
Then you can just compare playerToss and computerToss directly:
if self.playerToss > self.computerToss {
// the player wins!
}
Storing the values as Int will aid you in debugging in addition to making the comparison easier.
Magic Numbers
In your program, I think it is clear what 6 represents. In general though it is wise to avoid magic numbers (uncommented numbers) in your code. So you could write the rolls as:
self.playerToss = Int.random(in: des.indices) + 1
self.computerToss = Int.random(in: des.indices) + 1
By using des.indices to obtain the range 0...5 from the des array, you ensure that the code would work correctly even if the number of values in the des array changed.
I personally would still store the rolls in the range 1...6 for debugging purposes, but you could avoid the + 1 and - 1 by storing the rolls in the range 0...5.
Related
I am trying to find an efficient way to solve the find a missing number from an array. I implemented the following way it's O(n). Please write any codes that efficiently solves this, just for learning purpose.
func findMissingNo(arrA: [Int]) -> [Int] {
let firstIndex = arrA.first ?? 0
let lastIndex = arrA.last ?? 0
let rslt = Array(firstIndex...lastIndex)
let missingNoArray = rslt.filter{ !arrA.contains($0)}
return missingNoArray
}
findMissingNo(arrA: [11,12,14,15,16,18]) // Prints [13, 17] by looping 9 times
Quickly written and tested (in terms of times performances against your code, but not in term of possible edges cases/mistakes, for instance, if array is 0...10, it won't work, but I'll let you work on the edges cases, since I focused mainly on the main cases, cases which might be covered during an edit and the end of the question)
Your current code:
func findMissingNo(arrA: [Int]) -> [Int] {
let firstIndex = arrA.first ?? 0
let lastIndex = arrA.last ?? 0
let rslt = Array(firstIndex...lastIndex)
let missingNoArray = rslt.filter{ !arrA.contains($0)}
return missingNoArray
}
let numberArray = [11,12,14,15,18]
let missing1 = findMissingNo(arrA: numberArray)
print("Missing1: \(missing1)")
My attempt:
func findMissingNo2(arrA: [Int]) -> [Int] {
var missingNumbers: [Int] = []
guard arrA.count > 2 else { return missingNumbers }
for i in 0...arrA.count-2 {
var current = arrA[i]
let next = arrA[i+1]
if next != current + 1 {
current += 1
while current != next {
missingNumbers.append(current)
current += 1
}
}
}
return missingNumbers
}
let missing2 = findMissingNo2(arrA: numberArray)
print("Missing1: \(missing2)")
Creating a big batch:
var array = Array(0...1000)
for _ in 0...10 {
if let index = array.indices.randomElement() {
let value = array.remove(at: index)
print("removed: \(value)") //To check just in case that's the good value returned by the methods
}
}
Testing:
let date1 = Date()
for _ in 0...100 {
let missing = findMissingNo(arrA: array)
print(missing)
}
print(Date().timeIntervalSince(date1)) //18.617565035820007
let date2 = Date()
for _ in 0...100 {
let missing = findMissingNo2(arrA: array)
print(missing)
}
print(Date().timeIntervalSince(date2)) //0.09566605091094971
print("---End")
print("")
For the time, I got: 18.857954025268555 vs 0.09159696102142334, a big factor difference (~200 times).
Why is there such a big difference?
Because of
let missingNoArray = rslt.filter{ !arrA.contains($0)}
It means:
for each number in result, check if arrayA contains that number.
->
for each number in result, for each number in arrayA (with a stop condition, so it's not a full iteration, but "almost" in term of complexity) check if there is a match...
Here there is a "double" (which is in fact not double, but n?) iteration that you missed.
I tested first with bigger value (array from "0 to 100000"), but it was taking too much time, with that "low number of values", the difference can already be seen.
Instead, you could use a Set:
let missingNoArray = Array(Set(rslt).subtracting(Set(arrA))).sorted()
It's faster than you method in my tests, (double my solution (0.21 ~ 0.22) in time performances), but still much faster than yours.
I added the sorted(), which may or may not be important in your solution, but will add time consumption since Set aren't ordered.
For the edges cases (ie: [3], [3, 4], [3, 8])
guard arrA.count > 2 else { return missingNumbers }
==>
guard !arrA.isEmpty else { return [] }
guard arrA.count > 2 else {
if arrA[0] + 1 >= arrA[1] {
return []
} else {
return Array((arrA[0] + 1)...arrA[1]).dropLast() //Because last will be arrA[1] which is present)
}
}
I am quite new to Swift 3 and to programming languages in general. I have the following arrays inside an array and a variable income:
let testArray: [[Double]] = [
[0,0],
[1000,20.5],
[3000,21],
[4000,22.5],
]
var income: Double = 3500
What I want to do is something similar to the VLOOKUP function in Excel: I want to find in the first column of the arrays (i.e. 0, 1000, 3000, 4000) a number which is equal or immediately smaller than my variable. In this case, as income = 3500, the program should return 3000. I tried using filter() but I don't know how to work with the arrays inside the array. Any help appreciated.
You can proceed as follows.
Get the first column of the array:
let firstColumn = testArray.map { $0[0] }
print(firstColumn) // [0.0, 1000.0, 3000.0, 4000.0]
Restrict to those elements which are less than or equal to the
given amount:
let filtered = firstColumn.filter { $0 <= income }
print(filtered) // [0.0, 1000.0, 3000.0]
Get the maximal element of the filtered array. If the elements are
sorted in increasing order then you can use last instead of max():
let result = filtered.max()!
// Or: let result = filtered.last!
print(result) // 3000.0
Putting it all together:
let result = testArray.map { $0[0] }.filter { $0 <= income }.max()!
print(result) // 3000.0
A possible optimization is to combine map and filter into
flatMap:
let result = testArray.flatMap { $0[0] <= income ? $0[0] : nil }.max()!
print(result) // 3000.0
This code assumes that there is at least one matching element,
otherwise last! or max()! would crash. If that is not guaranteed:
if let result = testArray.flatMap( { $0[0] <= income ? $0[0] : nil }).max() {
print(result) // 3000.0
} else {
print("no result")
}
Or provide a default value (0.0 in this example):
let result = testArray.flatMap( { $0[0] <= income ? $0[0] : nil }).max() ?? 0.0
print(result) // 3000.0
Something like this:
let testArray: [[Double]] = [
[0,0],
[1000,20.5],
[3000,21],
[3500,22.5],
[3300,21],
]
let income: Double = 3500
var closest = testArray[0][0]
var closestDif = closest - income
for innerArray in testArray {
let value = innerArray[0]
let thisDif = value - income
guard thisDif <= 0 else {
continue
}
if closestDif < thisDif {
closestDif = thisDif
closest = value
guard closestDif != 0 else {
break
}
}
}
print(closest)
So basically I am looking to choose one of the 4 different coloured balls at random to come into the scene which each have an animation, physics properties and movement & spacing that I have already coded. I am not sure exactly how to make the array then choose at random from the array of the 4 coloured balls so that I have one ball chosen at random to come into the scene.
To make it more clear what I'm asking here's some code (I only use two balls in this code so you don't have to read as much):
var moveandremove = SKAction() < this is in my ballScene.swift
The spawn runBlock is inside didMovetoView
let spawn = SKAction.runBlock({
() in
self.allballs()
})
let delay = SKAction.waitForDuration(2.0)
let SpawnDelay = SKAction.sequence([spawn, delay])
let spawndelayforever = SKAction.repeatActionForever(SpawnDelay)
self.runAction(spawndelayforever)
let distance = CGFloat(brnball.frame.width * 20 + brnball.frame.width)
let moveball = SKAction.moveByX(-distance, y: 0, duration: NSTimeInterval(0.003 * distance))
let removeball = SKAction.removeFromParent()
moveandremove = SKAction.sequence([moveball])
}
func allballs() {
TextureAtlasblk = SKTextureAtlas(named: "blkball")
for i in 1...TextureAtlasblk.textureNames.count{
var Name = "blkball_\(i)"
blkarray.append(SKTexture(imageNamed: Name))
}
blkball = SKSpriteNode(imageNamed: "blkball_1")
blkball.position = CGPoint(x: CGRectGetMidX(self.frame) + 100, y: CGRectGetMidY(self.frame))
blkball.zPosition = 7
blkball.setScale(0.1)
self.addChild(blkball)
blkball.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(blkarray, timePerFrame: 0.2)))
//brownball
TextureAtlasbrn = SKTextureAtlas(named: "brnball")
for i in 1...TextureAtlasbrn.textureNames.count{
var Name = "brnball_\(i)"
brnarray.append(SKTexture(imageNamed: Name))
}
brnball = SKSpriteNode(imageNamed: "brnball_1")
brnball.position = CGPoint(x: CGRectGetMidX(self.frame) + 50, y: CGRectGetMidY(self.frame))
brnball.zPosition = 7
brnball.setScale(0.1)
self.addChild(brnball)
brnball.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(brnarray, timePerFrame: 0.2)))
Here is my terrible starting point at trying to make an array to choose from each ball (this is inside my allballs() function):
var ballarray: NSMutableArray = [blkball, brnball, yelball, bluball]
runAction(moveandremove)
I am new to swift and pretty hopeless, would be awesome if someone could help me out :)
Thanks
It's hard for me to find the array that you're talking about in your code. But nevertheless, I can still show you how.
Let's say we have an [Int]:
let ints = [10, 50, 95, 48, 77]
And we want to get a randomly chosen element of that array.
As you may already know, you use the subscript operator with the index of the element to access an element in the array, e.g. ints[2] returns 95. So if you give a random index to the subscript, a random item in the array will be returned!
Let's see how can we generate a random number.
The arc4random_uniform function returns a uniformly distributed random number between 0 and one less the parameter. Note that this function takes a UInt32 as a parameter and the return value is of the same type. So you need to do some casting:
let randomNumber = Int(arc4random_uniform(UInt32(ints.count)))
With randomNumber, we can access a random element in the array:
let randomItem = ints[randomNumber]
Try to apply this technique to your situation.
Here's a generic method to do this as well:
func randomItemInArray<T> (array: [T]) -> T? {
if array.isEmpty {
return nil
}
let randomNumber = Int(arc4random_uniform(UInt32(array.count)))
return array[randomNumber]
}
Note that if the array passed in is empty, it returns nil.
You could make and extension for Array that returns a random element.
extension Array {
func randomElement() -> Element {
let i = Int(arc4random_uniform(UInt32(count - 1)))
return self[i]
}
}
You could take that a step further and allow a function to be applied directly to a random element.
mutating func randomElement(perform: (Element) -> Element) {
let i = Int(arc4random_uniform(UInt32(count - 1)))
self[i] = perform(self[i])
}
You can use this function when using an array of reference types.
func randomElement(perform: (Element) -> ()) {
let i = Int(arc4random_uniform(UInt32(count - 1)))
perform(self[i])
}
So I am creating a card game, Ring of Fire. I have stored images like this:
var picture:[UIImage] = [
UIImage(named: "Card2")!,
UIImage(named: "Card3")!,
UIImage(named: "Card4")!,
UIImage(named: "Card5")!,
UIImage(named: "Card6")!,
UIImage(named: "Card7")!,
UIImage(named: "Card8")!,
UIImage(named: "Card9")!,
UIImage(named: "Card10")!,
UIImage(named: "CardJack")!,
UIImage(named: "CardQueen")!,
UIImage(named: "CardKing")!,
UIImage(named: "CardAce")!,
]
Each card has text displayed under the current card:
var name:String = ""
var files = ["Velg en som må drikke", // 2
"Drikk selv", // 3
"Alle jenter må drikke", // 4
"Tommelen", // 5
"Alle gutter må drikke", // 6
"Pek på himmelen", // 7
"Drikkepartner", // 8
"Rim", // 9
"Kategori", // 10
"Lag en regel", // Jack
"Spørsmålsrunde", // Queen
"Hell drikke i koppen", // King
"Fossefall"] // Ace
And this is how I pick a random card:
func imageTapped(img: AnyObject){
if(cardsleftLabel.text != "0") {
let randomNumber = Int(arc4random_uniform(UInt32(files.count)))
let image = picture[randomNumber]
cardImage.image = image
name = files[randomNumber]
}
else{
print("No more cards")
}
}
The problem is that the card may appear many times, and that is wrong. There are 4 of each card, so how can I control that in my game? So the CardJack don't appear 6 times?
One way to do it is to generate an array of indices that represent your cards. Shuffle that array, and then remove the indices from that array as you draw a card.
// generate random list of indices from 0...12 four each
var cardIndices = (0...51).map {($0 % 13, arc4random())}.sort{$0.1 < $1.1}.map{$0.0}
// To get a card, remove last card from deck
let last = cardIndices.removeLast()
// use the index to look up the picture
let randomCard = picture[last]
// It's also easy to check how many cards you have left in your deck
let remaining = cardIndices.count
This works by first creating an array of tuples which contain a number from 0...12 and a some random integer. Then that array is sorted by the random integer element in the tuple, and then map is used to separate out just the array of indices leaving you with a random array of Int with values from 0...12 (four values of each).
Here it is in class form.
import UIKit
struct Card {
let image: UIImage
let text: String
}
class Deck {
private let cards:[Card] = [
Card(image: UIImage(named: "Card2")!, text: "Velg en som må drikke"),
Card(image: UIImage(named: "Card3")!, text: "Drikk selv"),
Card(image: UIImage(named: "Card4")!, text: "Alle jenter må drikke"),
Card(image: UIImage(named: "Card5")!, text: "Tommelen"),
Card(image: UIImage(named: "Card6")!, text: "Alle gutter må drikke"),
Card(image: UIImage(named: "Card7")!, text: "Pek på himmelen"),
Card(image: UIImage(named: "Card8")!, text: "Drikkepartner"),
Card(image: UIImage(named: "Card9")!, text: "Rim"),
Card(image: UIImage(named: "Card10")!, text: "Kategori"),
Card(image: UIImage(named: "CardJack")!, text: "Lag en regel"),
Card(image: UIImage(named: "CardQueen")!, text: "Spørsmålsrunde"),
Card(image: UIImage(named: "CardKing")!, text: "Hell drikke i koppen"),
Card(image: UIImage(named: "CardAce")!, text: "Fossefall")
]
private var cardIndices = [Int]()
var cardsInDeck: Int { return cardIndices.count }
func shuffleCards() {
cardIndices = (0...51).map{($0 % 13, arc4random())}.sort{$0.1 < $1.1}.map{$0.0}
}
func drawCard() -> Card {
if cardIndices.count == 0 {
shuffleCards()
}
let last = cardIndices.removeLast()
return cards[last]
}
}
Notes:
The cards and cardIndices have been made private to hide those details from a user of Deck.
Thanks to #Paulw11's suggestion, this solution now uses a struct to represent a card. This keeps the data together and provides a nice value that can be returned from drawCard.
The user of a Deck can create a Deck with Deck(), they can call shuffleCards() to randomize the deck, check the cardsInDeck property to find out how many shuffled cards are available, and they can call drawCard() to get the next card from the deck.
How to Use
For the viewController that controls the deck, add a property to the viewController:
class MyGame: UIViewController {
var deck = Deck()
// the rest of the code
}
Then when you need a card, for example inside of an #IBAction for a button, just call deck.drawCard:
#IBAction func turnOverNextCard(button: UIButton) {
let card = deck.drawCard()
// Use the image and text to update the UI
topCardImageView.image = card.image
topCardLabel.text = card.text
// I'm not going to wait for the deck to shuffle itself
if deck.cardsInDeck < 10 {
deck.shuffleCards()
}
}
Splitting Hairs: A Better Shuffle
My shuffle routine shuffles the deck by associating a random UInt32 with each card and then sorting the deck by those values. If the same random number is generated for two cards, then it is possible that earlier cards in the deck would be favored over later cards (or vice versa depending on the sort algorithm). This is really splitting hairs, but in the interest of providing the best shuffle possible, I provide the following alternative:
func shuffleCards() {
cardIndices = (0...51).map {$0 % 13}
for i in (1...51).reverse() {
let rand = Int(arc4random_uniform(UInt32(i + 1)))
(cardIndices[i], cardIndices[rand]) = (cardIndices[rand], cardIndices[i])
}
}
This algorithm is based upon the Fisher-Yates shuffle.
You need a class to represent the Deck of cards, like this.
class Deck {
static let seeds = 4
var images : [UIImage:Int] = [
UIImage(named: "Card2")! : seeds,
UIImage(named: "Card3")! : seeds,
UIImage(named: "Card4")! : seeds,
UIImage(named: "Card5")! : seeds,
UIImage(named: "Card6")! : seeds,
UIImage(named: "Card7")! : seeds,
UIImage(named: "Card8")! : seeds,
UIImage(named: "Card9")! : seeds,
UIImage(named: "Card10")! : seeds,
UIImage(named: "CardJack")! : seeds,
UIImage(named: "CardQueen")! : seeds,
UIImage(named: "CardKing")! : seeds,
UIImage(named: "CardAce")! : seeds
]
func extractRandomCard() -> UIImage? {
let flatten = images.reduce([UIImage]()) { [UIImage](count: $0.1.1, repeatedValue: $0.1.0) }
guard !flatten.isEmpty else { return nil }
let random = Int(arc4random_uniform(UInt32(flatten.count)))
let selectedCard = flatten[random]
images[selectedCard] = images[selectedCard]! - 1
return selectedCard
}
}
Now you can extract cards from the deck writing
let deck = Deck()
let image = deck.extractRandomCard()
Each time you extract a card the Deck keeps track of it and it won't let you extract more than 4 times the same card.
I didn't test it... but it should work
An alternative approach to the random-stuff is to use some of Apple's nice GamePlayKit-stuff created exactly for these kind of use-cases. If you have the cards array you could for instance just do:
let shuffled2 = GKRandomSource().arrayByShufflingObjectsInArray(cards) as! [Card]
or you could keep the array unshuffled and instead shuffle the indexes you request:
let indexes = GKShuffledDistribution.init(forDieWithSideCount: 52)
// going through all the cards randomly
for _ in cards {
let card = cards[indexes.nextInt()]
}
public enum Palo: Int
{
case Corazones = 1
case Treboles = 2
case Picas = 3
case Diamantes = 4
}
public struct Card
{
public var text: String
public var position: Int
public var palo: Palo
public func description() -> String
{
return "\(self.position) of \(self.palo) -- \(self.text)"
}
}
public struct Deck
{
public var cards: [Card]
public init()
{
cards = [Card]()
for number in 1...13
{
for palo in 1...4
{
let card: Card = Card(text: "", position: number, palo: Palo(rawValue: palo)!)
cards.append(card)
}
}
}
/**
Return cards one by one
*/
public mutating func randomCard() -> Card?
{
guard !self.cards.isEmpty else
{
return nil
}
let position: Int = Int(arc4random_uniform(UInt32(self.cards.count)))
let card: Card = self.cards.removeAtIndex(position)
return card
}
}
//
// TEST
//
var deck: Deck = Deck()
for index in 1...200
{
if let card = deck.randomCard()
{
print("\(index) -- \(card.description())")
}
}
Array of Colors
let colorArray = [
UIColor.redColor(),
UIColor.orangeColor(),
UIColor.yellowColor(),
UIColor.greenColor(),
UIColor.blueColor()
]
The goal is to shift the array:
To start with a different color.
To preserve the circular order of colors.
Example #1
If we wanted to start with the orange color (the color at index 1 in the original array), the array would look like this:
let colorArray = [
UIColor.orangeColor(),
UIColor.yellowColor(),
UIColor.greenColor(),
UIColor.blueColor(),
UIColor.redColor(),
]
Example #2
If we wanted to start with the green color (the color at index 3 in the original array), the array would look like this:
let colorArray = [
UIColor.greenColor(),
UIColor.blueColor(),
UIColor.redColor(),
UIColor.orangeColor(),
UIColor.yellowColor()
]
Short & clear Swift 3 & 4 solution I came up with:
extension Array {
func shifted(by shiftAmount: Int) -> Array<Element> {
// 1
guard self.count > 0, (shiftAmount % self.count) != 0 else { return self }
// 2
let moduloShiftAmount = shiftAmount % self.count
let negativeShift = shiftAmount < 0
let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount
// 3
let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount }
// 4
return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element }
}
}
Explanation:
Arrays with no elements and shifts producing the identity of the
original array are returned immediately
To get the effective shift amount regardless of the amount passed with the function, we do some modulo calculation to get rid of shifts that would rotate the elements in the array more than once (e.g. in an Array with 5 Objects, a shift of +7 is the same as a shift of +2). Since we always want to shift to the right, in order to be done with one simple function instead of two, negative inputs have to be dealt with (e.g. in an Array with 5 Objects, a shift of -2 is the same as a shift of +3). Therefore we adjust the negative results of the modulo calculation by the length of the array.
Of course those 3 lines could be done in one, but I wanted to make this as readable as possible.
Now we prepare the actual shift by taking the index ($0) of element and returning the shifted index by adding the amount calculated in step 2. If the new index lands outside of the array length, it needs to be wrapped around to the front.
And finally we apply all the preparation to our array with some trickery: enumerated() gives us an array of tuples [(offset: Int, element: Int)], which is simply the original index of every element and the element itself. We then sort this enumerated array by the manipulated offset (aka the element's index) via applying the function from step 3. Lastly we get rid of the enumeration by mapping the sorted elements back into an array.
This extension works with arrays of any type. Examples:
let colorArray = [
UIColor.red,
UIColor.orange,
UIColor.yellow,
UIColor.green,
UIColor.blue
]
let shiftedColorArray = [
UIColor.green,
UIColor.blue,
UIColor.red,
UIColor.orange,
UIColor.yellow
]
colorArray.shifted(by: 2) == shiftedColorArray // returns true
[1,2,3,4,5,6,7].shifted(by: -23) // returns [3,4,5,6,7,1,2]
I know this might be late. But the easiest way to rotate or shift an array is
func shifter(shiftIndex: Int) {
let strArr: [String] = ["a","b","c","d"]
var newArr = strArr[shiftIndex..<strArr.count]
newArr += strArr[0..<shiftIndex]
println(newArr) }
shifter(2) //[c, d, a, b] you can modify the function to take array as input
You can extend Array to include a method to return an array containing the elements of the original array rotated by one element:
extension Array {
func rotate(shift:Int) -> Array {
var array = Array()
if (self.count > 0) {
array = self
if (shift > 0) {
for i in 1...shift {
array.append(array.removeAtIndex(0))
}
}
else if (shift < 0) {
for i in 1...abs(shift) {
array.insert(array.removeAtIndex(array.count-1),atIndex:0)
}
}
}
return array
}
}
To shifts the elements of an array once
let colorArray:[UIColor] = [
.redColor(),
.orangeColor(),
.yellowColor(),
.greenColor(),
.blueColor()
]
let z = colorArray.rotate(1)
// z is [.orangeColor(), .yellowColor(), .greenColor(), .blueColor(), .redColor()]
and twice
let z = colorArray.rotate(2)
// z is [.yellowColor(), .greenColor(), .blueColor(), .redColor(), .orangeColor()]
A variation of #zizutg's answer, that can shift in both directions (positive and negative)
extension Array {
public func shifted(by index: Int) -> Array {
let adjustedIndex = index %% self.count
return Array(self[adjustedIndex..<self.count] + self[0..<adjustedIndex])
}
}
// True modulo function https://stackoverflow.com/a/41180619/683763
infix operator %%
public func %%(_ dividend: Int, _ divisor: Int) -> Int {
precondition(divisor > 0, "modulus must be positive")
let reminder = dividend % divisor
return reminder >= 0 ? reminder : reminder + divisor
}
You can iterate by handling starting index.
func iterate<T>(array:Array<T>, start:Int, callback:(T) -> ()) {
let count = array.count
for index in start..<(start + count) {
callback(array[index % count])
}
}
If you want to start from index 3
iterate(colors, 3, { (color) -> () in println("color - \(color)")})