Generate random Strings without a shuffle and repetition of previous ones? - arrays

My code already generates a random String of an array when I press a button, but sometimes a String gets repeated. What do I have to do so that the String "Mango" only gets called again when all the other Strings where already called without using a shuffle, I want to call one String at a time?
Example: "Mango", "Kiwi", "Banana", "Pineapple", "Melon", "Mango", "Kiwi",.....
Here is my code:
var array = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
let fruits = Int(arc4random_uniform(UInt32(array.count)))
print(array[fruits])

In order to avoid repetitions you need to keep track of which fruits have previously been seen. There are several ways to do that an all of the proposed solutions do it in one way or another.
For your specific use case, you will need this tracking to be retained outside of the code executed by the button (in your view controller for example).
Here is a generalized structure that could help with this:
(you can define it inside the view controller if this is a one-time thing or outside of it if you intend to reuse the mechanism elsewhere)
struct RandomItems
{
var items : [String]
var seen = 0
init(_ items:[String])
{ self.items = items }
mutating func next() -> String
{
let index = Int(arc4random_uniform(UInt32(items.count - seen)))
let item = items.remove(at:index)
items.append(item)
seen = (seen + 1) % items.count
return item
}
}
To use it, you would declare a variable (in your VC) that will keep track of your fruits:
var fruits = RandomItems(["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"])
And in the button's code use that variable (fruits) to print a non-repeating fruit name at each execution
func buttonPressed() // <- use your function here
{
print( fruits.next() )
}

You need to implement some logic. It's quite easy if you think harder. Run this in your Playground, or if you fully understand this block of code, you can do this in your project already.
var array = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
var selectedIndices = [Int]()
for _ in 1...20 {
let randomFruitIndex = Int(arc4random_uniform(UInt32(array.count)))
// Print only if not yet printed once
if !selectedIndices.contains(randomFruitIndex) {
print(array[randomFruitIndex])
selectedIndices.append(randomFruitIndex)
}
// Reset
if selectedIndices.count == array.count {
print("----- CLEARING SELECTED INDICES----")
selectedIndices.removeAll()
}
}
So as you can see, we are adding each generated random number (in your case, it's the fruits variable.) into an array of Int. Then if the number of selectedIndices is equal to the count of the array of fruits, clear all the stored selectedIndices.
OUTPUT:
Pineapple
Melon
Mango
Kiwi
Banana
Apple
----- CLEARING SELECTED INDICES----
Mango
Melon

This is an adaption from the accepted answer of the linked topic in my comment:
var source = ["Mango", "Banana", "Apple","Kiwi", "Melon", "Pineapple"]
var usedElements = [String]()
func choosePseudoRandomElement() -> String {
if source.count == 0 {
source = usedElements
usedElements = []
}
let randomIndex = Int(arc4random_uniform(UInt32(source.count)))
let randomItem = source[randomIndex]
usedElements.append(randomItem)
source.remove(at: randomIndex)
return randomItem
}
for _ in 1...18 {
print("Item: \(choosePseudoRandomElement())")
}
One potential issue with this solution is that it may happen, that the last element of one complete iteration also occurs as the first element of the second iteration. You can handle that case by comparing the randomly chosen item with the item which was chosen before (use a while loop until the items doesn't match anymore).
Also, this does remove elements from the source array. If you do not want that, create a copy of the source array.

Related

Find objects that include an array that contains all elements of a second array

I'm trying to filter a set of objects based on values in one of their elements based on another array. I've got it working with filter just fine if the search is "OR" - it returns give me all objects for which at least one of the strings in the search array is found.
But I can't figure out how to make it work as an AND search - returning only the objects that match ALL of the strings in the search array.
Example:
struct Schedule {
let title: String
let classTypes: [String]
}
let schedule1 = Schedule(title: "One", classTypes: ["math","english","chemistry","drama"])
let schedule2 = Schedule(title: "Two", classTypes: ["pe","math","biology"])
let schedule3 = Schedule(title: "Three", classTypes: ["english","history","math","art"])
let schedules = [schedule1, schedule2, schedule3]
let searchArray = ["math", "english"]
//works for OR - "math" or "english"
var filteredSchedules = schedules.filter { $0.classTypes.contains(where: { searchArray.contains($0) }) }
I'd like to find a way for it to use the same search array
let searchArray = ["math", "english"]
But only return items 1 & 3 - as they both have BOTH math and english in the list.
There are good examples of AND conditions when the AND is across different search criteria: car type and colour - but I've been unable to find an example where the criteria are dynamically based on items in an array. For context, I could have dozens of schedules with 20+ class types.
You can work with a Set, isSubset will return true if the schedules element contains all elements of the searchSet
let searchSet = Set(searchArray)
var filteredSchedules = schedules.filter { searchSet.isSubset(of: $0.classTypes) }
As suggested by #LeoDabus it might be worth changing the type of classTypes to Set instead of arrays (if order doesn't matter) since they seems to be unique and then the filtering can be done in the opposite way without the need to convert searchArray each time
var filteredSchedules = schedules.filter { $0.classTypes.isSuperset(of: searchArray) }

How to fetch data from a dictionary

I have a list of dogs and need to fetch certain bits of data. In one case I need the row of names to show in a list, in other cases I need all or parts of the data from a single dog (name, gender, speed). I am fairly certain I should be using an array, although I started with a dictionary. I plan to add more parameters and allow users to add more dogs, so I am trying to find the most expandable option
struct Dog {
var name: String
var gender: String
var speed: Int
}
struct MyDogs {
let myDogs = [
Dog(name: "Saleks", gender: "Male", speed: 50),
Dog(name: "Balto", gender: "Male", speed: 70),
Dog(name: "Mila", gender: "Female", speed: 20)
]
}
WARNING I don't have my IDE available, may have a few syntax errors.
For reference, what you're demonstrating is not a multi-dimensional array. A 3d array is like this.
let some3DArray =
[["Hello", "World"],
["This", "Is", "An"],
["Multidimensional","Array"]]
To access the values in your example, based on what you're asking for you'd do it like so.
//To loop through all the dogs in your array. Useful for your "List"
for dog in yourDogs {
print(" Name: \(dog.name) "
}
// To find a dog based on some property you can do something like this.
let dog = {
for dog in yourDogs {
if dog.name == yourSearchValue {
return dog
} else {
//HANDLE NULL VALUE
//What do you want to happen if NO dog is found?
}
return null
}
}
// You can use the values from the array by accessing it directly via an index.
// This can be done with whatever conditional you need to specifically reach.
let specificDog = dogs[3]
// Once you have your copy of the specific dog you want to access.
// You can then get the values of that object.
let dogName = specificDog .name
let dogGender = specificDog .gender
let dogSpeed = specificDog .speed
Your use-case seems to be on the right track. An array would be useful and provide the most flexibility to add more dogs later down the road. This could be handled very easily for example by doing something like this. You can find out more about that here. Add an element to an array in Swift
var yourDogArray = [Dogs]()
yourDogArray.append(Dog(name: "xxx", gender: "female", speed: 20))
TableView(didSelectRowAt...)
This is a common usage And it works because your list that you populate is populated on an index from 0 to length which means if you select the first item on the list, it will match with your first item in your arrayCollection.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath {
let name = yourDogArray[indexPath.row].name
let gender = yourDogArray[indexPath.row].gender
let speed = yourDogArray[indexPath.row].speed
//Do whatever else you need to do here with your data. In your case you'd
//probably segue to the details view controller and present this data.
//Read up on Segue and Prepare for Segue to pass data between controllers.
}

Using returned value (array of Strings) in another Swift file

Apologize for a complete newbie question. This was the original array of Strings I wrote:
let fruit = ["apple1.jpg", "apple2.jpg", "apple3.jpg", ..... "apple10.jpg"]
First, I made a function in a separate Swift file (attached to the project) to replace above array, as the array's content might be changed based on several input factors later:
class Fruits {
let fruit = "apple"
func fruitName() -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
}
}
This is everything written on Fruits.swift file. And then, back to original file, I wanted to replace the original let fruit = ["", "", ...] to something like let fruit = Fruits.fruitName() - by loading the returned arrayA. But it was a bit confusing to understand how to use returned String Array values in a different file, inside of a different Class bracket. I tried something like let fruits = Fruits(), let fruit = fruits.fruitName(), etc but it doesn't seem to successfully replace the original array code. I still need to create the constant let fruit = part. Is there any way to load the returned value in a separate file? Much appreciated. <3
If you want a property of a class to be directly accessible from anywhere in your code, you can make it static:
class Fruits {
static let fruit = "apple"
static func fruitName() -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
}
// usage:
let fruits = Fruits.fruitName()
Depending on your specific situation, you could even not have a class and have a global function that takes the fruit as a parameter:
func fruitNames(fruit: String) -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
// usage:
let fruits = fruitNames(fruit: "apple")

iOS Swift: How to find unique members of arrays of different types based on specific attributes

Goal: I have two different classes, and two arrays containing members of each class. Using Swift 2.0, I would like to find the unique members of one array compared to the other based on specific attributes of each class.
Example:
class A {
var name: String
init(name: String) {
self.name = name
}
}
class B {
var title: String
init(title: String) {
self.title = title
}
}
let aArray = [A(name:"1"), A(name:"2"), A(name:"3"), A(name:"4")]
let bArray = [B(title:"1"), B(title:"2"), B(title:"5")]
So, I'm looking for some operation between aArray and bArray which returns the 3rd and 4th element of aArray, because they are uniquely in aArray, where the basis of comparison is the attributes A.name and B.title.
Of course, reversing the order of the operation would pick out the 3rd element of bArray, because it is uniquely in bArray.
I know I can accomplish the goal straightforwardly using a simple for loop, but I was hoping for something more elegant and more optimized. But if a for loop is as fast or faster than anything fancier, I'm happy to use it just as well.
I'm not sure fancy or elegant this code is, but, we could do something like this:
let mappedArray = bArray.map { $0.title }
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
So when we want the unique elements from aArray, we first map the elements from bArray to get an array of the value we want to actually compare:
let mappedArray = bArray.map { $0.title }
mappedArray is just an array of strings based on the title property of the objects in bArray.
Next, we use the filter method to filter objects from aArray. The filter method returns an array with objects that pass the test in our closure. The test we want to apply is objects that are not contained in the mapped array we just built.
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
If we want to do it the other way, just change a few things:
let mappedArray = aArray.map { $0.name }
let filteredArray = bArray.filter { !mappedArray.contains($0.title) }

Get Quote and its author using Swift

I am very new to Swift, and I am trying to make an app of quotes, but I want to display the Quote and its own author both in a separate UILabel. But I don't know how to do this, because I have a randomQuote function that returns a random index for my array but the array only displays the quote, I want to get a random quote and get the author of the quote.
Here is what I have done so far:
var quotes = ["'Stay Hungry, Stay Foolish.'", "'Life is what happens while you are busy making other plans.'", ""]
var authors = ["Steve Jobs", "John Lennon"]
func getRandomQuote() -> String{
//get random index
var arrayCount = UInt32(quotes.count)
var randomNumber = arc4random_uniform(arrayCount)
var finalNumber = Int(randomNumber)
return quotes[finalNumber]
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var randomImage: String = imageChange[randomNumber]
self.motivationText.text = getRandomQuote()
self.imageInspire.image = UIImage(named: randomImage)
}
I was thinking about using a dictionary but I think it won't work :/
If your authors and quotes are always in the same order you can use the same finalNumber for both the quotes & author array.
You can set both labels inside of getRandomQuote and remove the String return, then you can call it from viewDidLoad.
A dictionary would work, you can treat it's keys as an array for random reasons and then just use the key/value pair to populate your labels.
Example if quotes & authors are always in the same order:
func getRandomQuote() {
//get random index
var arrayCount = UInt32(quotes.count)
var randomNumber = arc4random_uniform(arrayCount)
var finalNumber = Int(randomNumber)
self.motivationText.text = quotes[finalNumber]
self.authorName.text = authors[finalNumber] // Just an assumption on label name
}
override func viewDidLoad() {
super.viewDidLoad()
getRandomQuote()
}
Dictionary example:
let quotesWithAuthors = ["Steve Jobs":"Stay Hungry, Stay Foolish","Alan Kay":"Simple things should be simple, complex things should be possible","Bill Gates":"I'm not fake Steve Jobs"]
let authorsArray = quotesWithAuthors.keys.array
let randomQuoteKeyIndex = Int(arc4random_uniform(UInt32(authorsArray.count - 1)))
self.authorName.text = authorsArray[randomQuoteKeyIndex]
self.otivationText.text = quotesWithAuthors[authorsArray[randomQuoteKeyIndex]]
In essence they are very similar from an execution point of view but may be easier to store this way. Though you may run into problems if you have multiple quotes from the same author using an author name as a dictionary - in that case you may want to flip the author & quote.

Resources