How can I not repeat the array when I click on the button in swift? I'm trying to generate fruits without them repeating.
import UIKit
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
#IBAction func fruitbutton(_ sender: UIButton) {
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear",]
let randomFruitgenerator = Int(arc4random_uniform(UInt32(array.count)))
fruitbox.text = array[randomFruitgenerator]
}
}
My suggestion is to use a Set and remove the random item from the set
var set = Set(["Apple","Banana","Orange","Pinapple", "Plum", "Pear"])
#IBAction func fruitbutton(_ sender: UIButton) {
if let fruit = set.randomElement() {
fruitbox.text = fruit
set.remove(fruit)
} else {
fruitbox.text = "" // or whatever to indicate that the set is empty
}
}
My suggest:
You can random each item 1 time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
#IBAction func fruitbutton(_ sender: UIButton) {
...
let fruitRandom = array random
array delete fruitRandom
if (array empty) {
array = originFruits
}
}
You can check and remove in next time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
let skipFruit = ""
#IBAction func fruitbutton(_ sender: UIButton) {
...
array = originFruits
array delete skipFruit
let fruitRandom = array random
skipFruit = fruitRandom
}
I think this will work, but I think it's Time Complexity can be O(n) as I am assuming that there is a possibility of randomElement return Apple every time and savedFruit is also Apple, so in that case, the Time Complexity will be O(n). A better workaround will be to remove that element from that array so, the next time the randomElement will be different for sure. Then, once it is different you can append the old one and remove the current one. I hope it makes sense to you, but for now this will work I think:
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
var savedFruit = String()
func fetchRandomFruit() {
if let fruit = array.randomElement() {
if fruit != savedFruit { //Here it will check if the last element is same as the new randomElement
savedFruit = fruit
fruitbox.text = savedFruit
} else {
fetchRandomFruit()
}
}
}
#IBAction func fruitbutton(_ sender: UIButton) {
fetchRandomFruit()
}
Use fruitbox.text = array.randomElement() ?? "default value for if array is empty". This is not guaranteed not to repeat, but is random.
If you want to ensure it does not repeat, use this
var new = array.randomElement() ?? fruitbox.text
while new == fruitbox.text {
new = array.randomElement() ?? fruitbox.text
}
fruitbox.text = new
This WILL loop infinitely (until it crashes) if the array is empty.
Related
So I'm working on a quiz-app and I want to give the user an option screen before starting the quiz, where the user can choose which categories the quiz should have.
Its currently only working with 1 category at a time but I want to be able to concatenate the questionArrays when the user selects button in the option screen.
At this time the user selects a button and depending on the sender.tag an Int gets passed through the var called 'pickedCategory' which then decides which of the arrays to base the questions on. This happens in the prepare for segue:
let selectedCategory = quizCategories[pickedCategory]
secondVC.allQuestions = selectedCategory.questions
quizCategories is declared at the top:
var quizCategories = [QuestionArray]()
What I would like to do is have 4 buttons and whenever one is selected, concatenate an array to the selectedCategory
For instance, button 1 & 2 is selected, i.e. the user wants to have questions based on 2 categories. Resulting in something like this?
let selectedCategory = quizCategories[0].questions + quizCategories[1].questions
and if 3 buttons selected, add another quizCategories array to the final array etc etc
Please note, its not HOW to concatenate I'm looking for, its how to automatically do this depending on buttons selected..
This is my first question posted and I am very new to Swift.. hope I didn't confuse u guys.. thanks :)
What I would suggest is for each category button, keep track of which is selected (and even deselected if the user decides they don't want that category), by adding or removing from an array of categoryIDs. Then, once the "Done" button (or whatever final decision button is tapped), take the array of categoryIDs they selected, and flatMap over them to produce the ultimate array of questions.
let questions = categoryIDs.flatMap { quizCategories[$0].questions }
Now, you'd have your array of questions to present to the user. Use flatMap vs map, because you'd want to flatten the returned array of arrays into a single array.
Tags are not the best way to identify buttons, but I'll ignore that.
Don't use buttons, use UISwitches. Have a switch for each category.
Say you have a constant switchTagBase, with a value of 100:
let switchTagBase = 100
So the switch for your first category has a tag of 100, the next one has a tag of 101, etc.
You already have an array of categories, which is good.
Now, when it's time to build your array of selected questions, simply go through the categories, figure out which switches are on, and add in those categories
var selectedCategories = [QuestionArray]()
for (index, array) in quizCategories.enumerated() {
let thisTag = index + switchTagBase
guard let thisSwitch = view.viewWithTag(thisTag),
thisSwitch.isOn else { continue }
selectedCategories += quizCategories[index]
}
(Note that you should really maintain an array of selection states based on the switches the user activates, and iterate through that array rather than looping through the switches directly, but in the interest of brevity I fetched switch states directly.)
You can track which button is selected and when you want to start quizz, concatenate the corresponding arrays.
Your code could look like this:
import UIKit
class QuizzSelection: UIViewController
{
#IBAction func firstButtonAction(_ sender: Any)
{
firstButtonWasClicked()
}
#IBAction func secondButtonAction(_ sender: Any)
{
secondButtonWasClicked()
}
#IBAction func goForwardWithQuizz(_ sender: Any)
{
startQuizzWasClicked()
}
var isFirstCategorySelected = false
var isSecondCategorySelected = false
let firstCategoryQuestions = ["Question Cat 1"]
let secondCategoryQuestions = ["Question Cat 2"]
var emptyArrayOfQuestions = [String]()
func firstButtonWasClicked()
{
if isFirstCategorySelected == true{
isFirstCategorySelected = false
} else{
isFirstCategorySelected = true
}
}
func secondButtonWasClicked()
{
if isSecondCategorySelected == true{
isSecondCategorySelected = false
} else{
isSecondCategorySelected = true
}
}
func startQuizzWasClicked()
{
if isFirstCategorySelected == true{
emptyArrayOfQuestions += firstCategoryQuestions
}
if isSecondCategorySelected == true{
emptyArrayOfQuestions += secondCategoryQuestions
}
}
}
EDIT
Improved code for six categories:
import UIKit
class QuizzSelection: UIViewController
{
#IBAction func firstButtonAction(_ sender: Any)
{
firstButtonWasClicked()
}
#IBAction func secondButtonAction(_ sender: Any)
{
secondButtonWasClicked()
}
// Four mour button actions
#IBAction func goForwardWithQuizz(_ sender: Any)
{
startQuizzWasClicked()
}
var wichCategoryAreSelected = [false, false, false, false, false, false] //six categories
var arrayOfQuestions = [["Question 1 Cat 1","Question 2 Cat 1"], ["Question 1 Cat 2", "Question 2 Cat 2"], ...]
var emptyArrayOfQuestions = [String]()
func firstButtonWasClicked()
{
wichCategoryAreSelected[0] = !wichCategoryAreSelected[0]
}
func secondButtonWasClicked()
{
wichCategoryAreSelected[1] = !wichCategoryAreSelected[1]
}
func startQuizzWasClicked()
{
for i in 0...(wichCategoryAreSelected.count-1)
{
if wichCategoryAreSelected[i]
{
emptyArrayOfQuestions += arrayOfQuestions[i]
}
}
}
}
My code has 2 textfields one for int and the other for string. The way the code is supposed to be sorted is by ascending alphabetical order for the string (a-z)then by descending order for the int (9-1).So i entered 2 entries in a,2 a,1 order but the way the list is being displayed is a,2 a,1 which is not the sorted order. How can I keep the sorted order?
VIEWCONTROLLER
#IBAction func move(_ sender: Any) {
yourArray.append((textA.text!))
number.append(Int(textB.text!)!)
let tuples = zip(yourArray,number)
let sorted = tuples.sorted(by: { this, next in
if this.0 < next.0 {
return true
} else if this.0 == next.0 {
return this.1 < next.1
} else {
return false
}})
bad.mm.append(String(describing: sorted.map { " \($0)" }.joined(separator:"\n")))
}
struct bad {
static var mm = [String]()}
VIEWCONTROLLER2
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
defaults.set(benCarson.text, forKey: "SavedStringArray2")
defaults.synchronize()
benCarson.text = String(describing: bad.mm)
benCarson.numberOfLines = 5000
}
I'm trying to figure out how the array works with Swift. I understand that you use let to create an immutable array and var to create a mutable array. Yet, Swift's array is not quite the same as Objective's NSArray and NSMutableArray. I get that.
In the example that follows, I create a mutable array with one element. Actually, I want to start with no element. But if I do, I won't be able to add a new string to it. If I start with one element, then I won't be able to add a new string to it after the original element is removed. So what am I doing wrong?
Thank you
EDIT
class ViewController: UIViewController {
let textCellIdentifier = "TextCell"
var myArray:[String] = ["GGG"] // or var myArray:[String] = []
#IBAction func add1Tapped(sender:AnyObject) {
let index = tableView1.indexPathForSelectedRow;
let selectedRow = index()?.row
if selectedRow < 0 {
return
} else {
let txt = nameField1.text
myArray.append(txt)
tableView1.reloadData()
}
}
func tableView(tableView: UITableView,numberOfRowsInSection section:Int) -> Int {
return myArray.count
}
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell=UITableViewCell(style: UITableViewCellStyle.Subtitle,reuseIdentifier:textCellIdentifier)
let row = indexPath.row
cell.textLabel!.text = myArray[row]
return cell
}
}
all your needs should work as expected:
// create an empty array of strings
var myArray: [String] = []
// add two elements
myArray.append("the first element")
myArray.append("the second element")
// remove both elements
myArray.removeAll()
// add another element
myArray.append("the third but now first element")
myArray.count
EDIT
try and change your add method like this:
#IBAction func add1Tapped(sender:AnyObject) {
if let _ = tableView1.indexPathForSelectedRow, txt = nameField1.text {
print("will append \(txt) to myArray")
myArray.append(txt)
tableView1.reloadData()
}
}
I recently started learning Swift which is my first attempt to learning how to program and I started my first app. I used several tutorials and so far I could solve every problem through research. Now I am stuck.
I want to create an app that can pick a random cocktail for me out of an array (cocktails) based on filters. To do so, I created 21 filters (cocktail.filter({!$o.orangeJuice}) for example. This takes all cocktails out of the array using orange juice.).
Creating the UI I added 21 UISwitches to toggle whether a filter has to be applied or not.
My randomize button works and there is a random cocktail name displayed but I can't get those UISwitches to work.
See my code:
var cocktailNoOrangeJuice = cocktails.filter({!$0.orangeJuice})
var cocktailNoLemonJuice = cocktails.filter({!$0.lemonJuice})
var cocktailNoAppleJuice = cocktails.filter({!$0.appleJuice})
var cocktailNoMaraJuice = cocktails.filter({!$0.maraJuice})
var cocktailNoLimeJuice = cocktails.filter({!$0.limeJuice})
var cocktailNoBananaJuice = cocktails.filter({!$0.bananaJuice})
var cocktailNoPeachJuice = cocktails.filter({!$0.peachJuice})
var cocktailNoCherryJuice = cocktails.filter({!$0.cherryJuice})
var cocktailNoJohanJuice = cocktails.filter({!$0.johanJuice})
var cocktailNoMangoJuice = cocktails.filter({!$0.mangoJuice})
var cocktailNoGrapefJuice = cocktails.filter({!$0.grapefJuice})
var cocktailNoTomatoJuice = cocktails.filter({!$0.tomatoJuice})
var cocktailNoCranbJuice = cocktails.filter({!$0.cranbJuice})
var cocktailNoBloodJuice = cocktails.filter({!$0.bloodJuice})
var cocktailNoPineapJuice = cocktails.filter({!$0.pineapJuice})
var cocktailNoCola = cocktails.filter({!$0.cola})
var cocktailNoSprite = cocktails.filter({!$0.sprite})
var cocktailNoBitter = cocktails.filter({!$0.bitter})
var cocktailNoTonic = cocktails.filter({!$0.tonic})
var cocktailNoGinger = cocktails.filter({!$0.ginger})
var cocktailNoAlc = cocktails.filter({!$0.noalc})
//this is a new array currently with the "noalc"-filter applied
var cocktailfiltered = cocktails.filter({!$0.noalc})
class ViewController: UIViewController {
// this is one of the UISwitches
#IBOutlet weak var lemon: UISwitch!
// The label for Cocktail output and the random button
#IBOutlet weak var ergebnis: UILabel!
#IBAction func random(sender: AnyObject) {
let randomIndex = Int(arc4random_uniform(UInt32(cocktailfiltered.count)))
ergebnis.text = (cocktailfiltered[randomIndex].name)
}
}
Please forgive me if this too silly. I found out how to pick up the state of a UISwitch (e.g lemon.on ...) but cannot use this information to apply a filter.
Any help is highly appreciated. Though I first hoped to be able to solve this on my own now it gets frustrating.
Notice that the cocktails are defined as members of a class and every ingredient such as orange juice throws a bool. Thus the filters are manually working. But not in the UI.
Edit: So this is the version right now. In my opinion it looks far better thanks to #vadian but causes my app to crash.
class ViewController: UIViewController {
let keys = ["lemonJuice", "limeJuice", "bananaJuice", "pineapJuice", "maraJuice", "mangoJuice", "orangeJuice", "appleJuice", "peachJuice", "bloodJuice", "grapefJuice", "tomatoJuice", "cranbJuice", "cherryJuice", "johanJuice", "cola", "sprite", "bitter", "tonic", "ginger", "noalc"]
var states = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]
#IBAction func changeState(sender: UISwitch) {
let index = sender.tag
states[index] = sender.on}
#IBOutlet weak var ergebnis: UILabel!
#IBAction func random(sender: AnyObject) {
var conditions = [String]()
for (index, state) in states.enumerate() {
if state {
conditions.append("(\(keys[index]) == TRUE)")
}
}
let format = conditions.joinWithSeparator(" AND ")
let predicate = NSPredicate(format:format)
let filtered = (cocktails as NSArray).filteredArrayUsingPredicate(predicate)
let randomIndex = Int(arc4random_uniform(UInt32(filtered.count)))
ergebnis.text = (filtered[randomIndex].name)
}
}
Suggestion:
Create two arrays, one for the names/keys of the Bool properties in your custom class
let keys = ["orangeJuice", "lemonJuice" ... ]
and one for the states of the corresponding switches
var states = [false, false, ...]
Assign tags to the UISwitches starting with zero in order of the keys array.
Create a change state IBAction and assign the action to all UISwitches
#IBAction func changeState(sender: UISwitch) {
let index = sender.tag
states[index] = sender.on
}
in the random function create an NSPredicate programmatically by a repeat loop to get all true values of the states array and the corresponding key of the keys array. Then filter the cocktail array by that predicate and get the random cocktail.
PS: For a good user experience get the filtered cocktails in the changeState function and inform the user in case no cocktail matches the chosen ingredients.
Update:
An example to create the predicate
var conditions = [String]()
for (index, state) in states.enumerate() {
if state {
conditions.append("(\(keys[index]) == TRUE)")
}
}
let format = conditions.joinWithSeparator(" AND ")
let predicate = NSPredicate(format:format)
I am stuck for a very long time. I am trying to implement a vote feature in a collection view. If the user taps the button it adds one vote to parse and shows it on the label. My code does that however when I look into the parse dashboard I see that a new row is create and the number of votes is not going into the post
My code for the cell is
import UIKit
import ParseUI
import Parse
var votes = [PFObject]()
class NewCollectionViewCell: UICollectionViewCell {
var parseObject = PFObject(className: "Posts")
#IBOutlet weak var postsImageView: PFImageView!
#IBOutlet weak var postsLabel: UILabel!
#IBOutlet weak var votesLabel:UILabel?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
postsLabel.textAlignment = NSTextAlignment.Center
print("Passing11")
}
#IBAction func vote(sender: AnyObject) {
if let votes = parseObject.objectForKey("votes") as? Int {
parseObject.setObject(votes + 1, forKey: "votes")
parseObject.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "\(votes + 1) votes"
print("Passing22")
}
else
{
parseObject.setObject(1, forKey: "votes")
parseObject.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "1 votes"
print("Passing33")
}
}}
and collection view is
if let votes = parseObject.objectForKey("votes") as? Int {
cell.votesLabel?.text = "\(votes) votes"
}
else
{
cell.votesLabel?.text = "0 votes"
}
return cell
}
How can I make it work? Thank you.
From what I remember in my Parse project. If you need to retrieve and update an existing row in Parse you need to create a PFQuery object first and retrieve the desired row using that query object. And then you can update its "vote" or whatever attribute value you want to. Kindly try that.