Swift, get variable name from a string - ios

I have a question. I know that this can be done in Ruby, not sure about Swift, but never did anything similar.
What I'm working on is a program that works similar to this: if the user writes in a TextView "a + b", my code should elaborate the value of the sum between the variable "a" and "b".
What I mean is, how can I get variable names from a string? Is it possible at all?
If it's not, I'd use a switch (variable names are limited), but I'd need to "connect" a variable to the one written by the user. In the example, if the user writes "a + b", my sum function is something like "sum(par1,par2)", so, how can I make variable "par1" point to "a" and "par2" point to "b"? I can do that with pointers right? Is there any other way?
I'm not sure I've been clear, but it's quite hard to explain

Check this out. You can do with NSExpression and KVC.
class myclass:NSObject {
var a = 10;
var b = 20;
override init() {
super.init();
}
}
var obj = myclass();
// Expression from user
var expr:NSString = "a+b" ;
//Operators..
let opSet = NSMutableCharacterSet()
opSet.addCharactersInString("+-*/");
let components:NSArray = expr.componentsSeparatedByCharactersInSet(opSet)
let uniqueChars:NSSet = NSSet(array: components)
for variable in uniqueChars
{
expr = expr.stringByReplacingOccurrencesOfString ( variable as String,
withString : NSString(format: "%d", obj.valueForKey(variable as String) as Int),
options : NSStringCompareOptions.LiteralSearch,
range : NSMakeRange(0,expr.length) );
}
var result: AnyObject = NSExpression(format: expr).expressionValueWithObject(nil, context: nil)
print (result);

Related

keeping two arrays in sync?

I am currently trying to keep my two arrays in sync. One question label and a one text label..
For my question label, I used arc4Random to pick a random question from my array. However, in order to set the label just above it in the UI, I need it to match my other array.. (titleQuestions)
How do I pull out the titleQuestion by using the arc4random number I generate from the questions array..
Sorry in advance, I am relatively new to Swift and still learning.. first year..
//just vars to get data from my two models(i.e two arrays)
var questionArray = DataModel()
var tArray = TitleLabel()
// this here is questions for the lower UI label. random No generate here!
func questions() {
let questions = questionArray.questions
let randomQuestions = questions.index(questions.startIndex , offsetBy : questions.count.arc4random)
questionLabel.text = questions[randomQuestions]
}
// the questions marks is because I dont know what to index here because I want this number but cant seem to get it!
func titles() {
let titlesArray = tArray.titleQuestions
titleLabel.text = titlesArray[?????]
}
// When this button is pressed both UIlabel will be updated with a random question and the associated title
#IBAction func questionsButton(_ sender: UIButton) {
questions()
titles()
}
I understand this could might be a stupid question but I have wasted a better part of a week trying to work this out.. any help is encouraged! thought about using a closure to take the questions function but really dont know how to implement it..
Welcome to SO. this is a trivial problem, and one you should really work out on your own, but it sounds like you're stuck, so I'll take pity on you.
At the simplest, take the code that generates a random number out of the line that fetches an item, and instead assign the random number to a temp variable, and set both labels from a single function:
func questionsAndTitles() {
let randomInt = Int(arc4random_uniform(Uint32(questions.count)))
let questions = questionArray.questions
questionLabel.text = questions[randomInt]
let titlesArray = tArray.titleQuestions
titleLabel.text = titlesArray[randomInt]
}
(Assuming questions is of type Array, there's no reason to create an Index object in order to fetch an item from the array. Arrays allow integer subscripting.)
However, it might make more sense to create an array of structures that contain both a question and an answer. That way you would fetch an instance of a question-and-answer struct from your array and use it to populate both.
Edit:
If you wanted to create an array of structs it might look like this:
struct QuestionStruct {
let question: String
let possibleAnswers: [String]
let correctAnswerIndex: int
}
var questions: [QuestionStruct] =
[
QuestionStruct(
question: "What is the airspeed velocity of an unladen swallow?",
possibleAnswers: ["3 ft/sec", "2.2 ft/sec", "European or African"],
correctAnswerIndex: 2),
QuestionStruct(
question: "What is Your favorite color?",
possibleAnswers: ["Blue", "Green", "Blue. No, Green! Ieee!"],
correctAnswerIndex: 2)
]
//And then you might use code like this:
let randomIndex = Int(arc4random_uniform(UInt32(questions.count)))
let aQuestion = questions[randomIndex]
let correctAnswerIndex = aQuestion. correctAnswerIndex
questionLabel.text.text = aQuestion.question
correctAnswerLabel.text = aQuestion.possibleAnswers[correctAnswerIndex]
You can store your picked random number in a class variable so it is accessible both from your questions() and titles() methods.
Use a property declared at the class scope :
var lastRandomSeed: Int?
Reuse it in both of your methods :
if let lastRandomSeed = lastRandomSeed {
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
lastRandomSeed = nil
} else {
lastRandomSeed = questions.count.arc4random
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
}
and :
if let lastRandomSeed = lastRandomSeed {
titleLabel.text = titlesArray[lastRandomSeed]
lastRandomSeed = nil
} else {
lastRandomSeed = questions.count.arc4random
titleLabel.text = titlesArray[lastRandomSeed]
}
This also ensures you can call your methods in both order.
A better approach is to refactor your code, use computed property that will update the view :
var lastRandomSeed: Int = -1 {
didSet {
let questions = questionArray.questions
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
let titlesArray = tArray.titleQuestions
questionLabel.text = questions[lastRandomSeed]
titleLabel.text = titlesArray[lastRandomSeed]
}
}
Just write it in your IBAction :
let questions = questionArray.questions
lastRandomSeed = questions.count.arc4random
You had to discipline yourself first. Try to name your variables, functions and even local variables by it's purpose. So at any point you can understand whats this variable/function/Type is used for.
So in you example I can assume that DataModel type is subclass of an array, because you assigning it's instance to variable questionArray. It is not very straightforward, but more or less ok (however better to name it DataArray). But then one who looking at you code can realise that your array not array. It is object that have array of questions in it. So you either had to name it let questionArrayHolder = DataModel() or fill it with questions: let questionArray = DataModel().questions.
But then you assign instance of TitleLabel to variable tArray. t can mean anything, and it definitely doesn't look like array. If you want to create array with single object in it, declare it titleArray = [TitleObjects]
Then you have function question()
It's name naturally suggests that it had to return some questions. But it returns nothing. If you want to generate questions name you function appropriate: func generateQuestions() . But your function do not generate questions, it picking up one question and assigning it to title. So you had name it like func pickupRandomQuestion().
You have lot of opportunities to share soothing between functions. You can declare member variable var selectedQuestion: Int? = 0 and assign your arc4rand number to it before retrieving you question.
You can find index of you question in array, assuming that all questions are unique, questions.index(of: questionLabel.text)
And you can return question number from you function:
func pickupRandomQuestion() -> Int {
let randNumber = questions.count.arc4random // I'm assuming that you have that function, and it works for you as you expect
let randomQuestion = questions.index(questions.startIndex , offsetBy : randNumber)
questionLabel.text = questions[randomQuestion]
return randNumber
}
And then supply it to other function
func pickupTitleForQuestion(at index: Int) {
titleLabel.text = titlesArray[index]
}

How to add elements to an array of custom objects in Swift

I am using this code to add custom objects to an array and then display that data in a custom TableView.
var tempCount = self.people?.count
for var k = 0 ; k < tempCount ; k++
{
if let PERSON = self.people?[k]
{
let name = (PERSON.compositeName != nil) ? PERSON.compositeName : ""
let number = (PERSON.phoneNumbers?.first?.value != nil) ? PERSON.phoneNumbers?.first?.value : ""
let image = (PERSON.image != nil) ? PERSON.image : UIImage(named: "aks.jpg")
let details = Contact(userImage: image!, userName: name!, phoneNumber: number!)
println(details.userName + " " + details.phoneNumber)
self.arrayOfContacts?.append(details)
println(self.arrayOfContacts?.count)
}
}
The count of the elements in the array always seems to be 'nil' for some reason. I have declared the array in the following manner
var arrayOfContacts:[Contact]?
, Contact being the type of Object that array is supposed to contain.
and the other one as
var people : [SwiftAddressBookPerson]? = []
The print statement does give out results but the object never gets added into the array.
Any idea about what I am doing wrong would be greatly helpful.
Your array is declared as an Optional array but is not created so it's nil.
Your declaration:
var arrayOfContacts:[Contact]?
Add the creation of an actual empty array:
arrayOfContacts = []
Or create it at once altogether:
var arrayOfContacts:[Contact]? = []
Your arrayOfContacts is nil, so arrayOfContacts?.count is nil as well.
If you really want to append to arrayOfContacts, don't write self.arrayOfContacts?.append(details) because this means "append to arrayOfContacts but actually I don't really care and if arrayOfContacts is nil, just give up".
Instead, write self.arrayOfContacts!.append(details), because now this means "append to arrayOfContacts, and since I really do care, tell me hard and loud, with a fatal crashing error, when arrayOfContacts is nil because well I'll have to figure out why on hell this array is nil when it should not be. I mean, I'm the master of the machine, not the opposite, and I know quite well that arrayOfContacts ought to be not nil when I want to append to it."
Often when you’re dealing with data you don’t just have a fixed amount of elements. Take for example a program where you compute the average of multiple grades in a class:
var grade1 = 4
var grade2 = 3
var average = Double(grade1 + grade2) / 2.0
println("Average grade: \(average)")
What if we wanted the program to also work when we have 3 grades?
We’d have to change our program to work with 3 grades.
var grade1 = 4
var grade2 = 3
var grade3 = 5
var average = Double(grade1 + grade2 + grade3) / 3.0
println("Average grade: \(average\)")

I know using a variable within a variable in Swift is a no no... but

Ok. Newbie here. I was trying to use a variable within a variable and finally did some searching on here and realized it was a no no... but maybe someone can help me think of a different way to do this.
Again, maybe Im thinking incorrectly in handling this so please feel free to laugh, make fun, or rip on me. :)
I have 4 UIButtons
#IBOutlet var westButton: UIButton!
#IBOutlet var northButton: UIButton!
#IBOutlet var southButton: UIButton!
#IBOutlet var eastButton: UIButton!
When one is clicked I am trying to access a class of places to load the next place. For example.
class Place {
var Name: String = ""
var northDirection: String = ""
var southDirection: String = ""
var eastDirection: String = ""
var westDirection: String = ""
init (Name: String, northDirection: String, southDirection: String, eastDirection: String, westDirection: String) {
self.Name = Name
self.northDirection = northDirection
self.southDirection = southDirection
self.eastDirection = eastDirection
self.westDirection = westDirection
}
...
...
let Home = Place (Name: "Home", northDirection: "North", southDirection: "South", eastDirection: "East", westDirection: "Park")
let Park = Place (Name: "Park", northDirection: "North", southDirection: "South", eastDirection: "Home", westDirection: "West")
What I was doing in the IBAction was trying to take the current 'Place' and get the corresponding place from its class. For example starting at 'Home' geting home.westDirection would be Park, but would like to load the westDirection value of Park to the next Title in the button.
I was hoping to use something like getting title of the west button when it is pressed. ( I am trying to set the new title of the button to the .westDirection value in the class of the title of the button. (Example from above, from the place Home, pressing westButton would return Park.
westButton.setTitle(("\(westButton.currentTitle!).westDirection"), forState: UIControlState.Normal)
Of course the above returns... "Park.westDirection" since its a string. I was playing with it to try to get the variable within a variable.
I am thinking I have to go back to the drawing board, maybe get away from a class, I was just hoping to have the IBAction kind of automatically fill in the values from the class. ( Maybe Im thinking too much like perl ) I dont know. Anyone have any ideas to help me figure this out a different way?
In Swift, the strong typing means you've got to approach this pretty differently. First of all, If you've got a type that has within it references to other types, that means recursion in Swift ("variable within a variable"). And, since this case isn't very well suited to recursion, that's going to mean headaches. As an example, to encode something like this map:
___ ___
| a | b |
|___|___|
| c | d |
|___|___|
Using recursion would involve this:
class Place { var north, east, south, west: Place? }
var (a, b, c, d) = (Place(), Place(), Place(), Place())
(a.east, a.south) = (b, c)
(b.west, b.south) = (a, d)
(c.north, c.east) = (a, d)
(d.north, d.east) = (b, c)
Now, that gives you the kind of behaviour you want (kind of). If you look for what's south of a, you just use its property:
a.south
And you can add in any other properties you need. (like a name, say). However, being able to access the places via their names would require a dictionary:
let places = ["Home":a, "Work": b, "Gym":c, "Pub":d]
And then you could access the name of the thing itself like this:
places["Home"]?.south?.someProperty
Where someProperty is a property of the class Place that you're interested in. However, as you can see, this is pretty tedious, and cumbersome. There's a lot of repetition. (you have to say a.east = b as well as b.west = a, for instance) A better approach would be to adopt a data structure that represents the map, and then access the things you want via that. Here's what I managed:
struct Map {
private let matrix: [[String]]
private let locations: [String:(Int, Int)]
private let xBounds, yBounds: ClosedInterval<Int>
init(_ locs: [[String]]) {
self.matrix = locs
var dict: [String:(Int, Int)] = [:]
for (y, row) in locs.enumerate() {
guard row.endIndex == locs[0].endIndex else { fatalError("All rows must be equal length") }
for (x, loc) in row.enumerate() {
dict[loc] = (x, y)
}
}
self.locations = dict
self.xBounds = ClosedInterval(0, locs[0].endIndex - 1)
self.yBounds = ClosedInterval(0, locs.endIndex - 1)
}
subscript (x: Int, y: Int) -> String? {
return (xBounds ~= x && yBounds ~= y) ? matrix[y][x] : nil
}
subscript(loc: String) -> (Int, Int)? { return locations[loc] }
enum Direction: Int { case North = 0, East, South, West }
private func shift(dir: Direction, from: (Int, Int)) -> (Int, Int) {
switch dir {
case .North: return (from.0, from.1 - 1)
case .South: return (from.0, from.1 + 1)
case .East: return (from.0 + 1, from.1)
case .West: return (from.0 - 1, from.1)
}
}
func adjacentTo(from: String, dir: Direction) -> String? {
return locations[from].flatMap {
self[shift(dir, from: $0)]
}
}
func adjacentTo(from: String) -> [String] {
return (0..<4).flatMap {
adjacentTo(from, dir: Direction(rawValue: $0)!)
}
}
}
It's probably overkill. But it allows you to have pretty much the behaviour you were looking for. Here's how you'd use it:
// To set up, give it a matrix
let map = Map([
["Home", "Work"],
["Gym" , "Pub" ]
])
Your matrix can be any size, but all of the rows must be the same length. Then, go ahead and use its methods:
// Find a place in a given direction
map.adjacentTo("Home", dir: .East) // "Work"
// Find all adjacents to a given place
map.adjacentTo("Pub") // ["Work", "Gym"]
Or, on a bigger map:
let map = Map([
["Home", "Work" , "School" ],
["Gym" , "Pub" , "Cafe" ],
["Pool", "Cinema", "Library"]
])
map.adjacentTo("Cinema", dir: .East) // "Library"
map.adjacentTo("Pub") // ["Work", "Cafe", "Cinema", "Gym"]
// Using the coordinate system
map["Home"] // (0, 0)
map[ 1, 1 ] // "Pub"
I think the simplest way is:
In the viewDidLoad(), load all the places you have (i.e. Home and Park), and store it them in an array (so array will be of type [Place]).
In your button #IBAction call (and before this implement) a custom method called something like findByPlaceName, that given a name it will find and return the Place on the array that has that name.
Change the buttons titles from that Place returned.
I think that's what you've been asking, right? Hope it helped!

Find Object with Property in Array

is there a possibility to get an object from an array with an specific property? Or do i need to loop trough all objects in my array and check if an property is the specific i was looking for?
edit: Thanks for given me into the correct direction, but i have a problem to convert this.
// edit again: A ok, and if there is only one specific result? Is this also a possible method do to that?
let imageUUID = sender.imageUUID
let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!
// this is working
//var imageObject:Images!
/*
for (index, image) in enumerate(questionImageObjects) {
if(image.imageUUID == imageUUID) {
imageObject = image
}
}
*/
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
Yes, you can use the filter method which takes a closure where you can set your logical expression.
Example:
struct User {
var firstName: String?
var lastName: String?
}
let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];
let johns = users.filter( { return $0.firstName == "John" } )
Note that filter returns an array containing all items satisfying the logical expression.
More info in the Library Reference
Here is a working example in Swift 5
class Point{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]
// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}
// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}
Reference:
Trailing Closure
Here is other way to fetch particular object by using object property to search an object in array.
if arrayTicketsListing.contains({ $0.status_id == "2" }) {
let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
print(ticketStatusObj.status_name)
}
Whereas, my arrayTicketsListing is [TicketsStatusList] contains objects of TicketsStatusList class.
// TicketsStatusList class
class TicketsStatusList {
internal var status_id: String
internal var status_name: String
init(){
status_id = ""
status_name = ""
}
}

Converting Character in an array to an Integer

I can't seem to figure out how to do this even though I've searched through documentation.
I'm trying to figure out how to convert a character at an index in an array to an integer.
For example, say I have a character array named "container", I can't figure out how to do:
var number:Integer = container[3]
Thanks for the help!
Swift doesn't make it easy to convert between primitive and typed representations of things. Here's an extension that should help in the meantime:
extension Character {
func utf8Value() -> UInt8 {
for s in String(self).utf8 {
return s
}
return 0
}
func utf16Value() -> UInt16 {
for s in String(self).utf16 {
return s
}
return 0
}
func unicodeValue() -> UInt32 {
for s in String(self).unicodeScalars {
return s.value
}
return 0
}
}
This allows you to get pretty close to what you want:
let container : Array<Character> = [ "a", "b", "c", "d" ]
/// can't call anything here, subscripting's also broken
let number = container[2]
number.unicodeValue() /// Prints "100"
For any engineers that come across this question, see rdar://17494834
I am not sure that it is effective or not but at least it worked. I converted Character to String then to Int.
String(yourCharacterInArray).toInt()
You may try this:
var container = "$0123456789"
var number:Int = Array(container.utf8).map { Int($0) }[3]
It's totally ugly, but it does the job. Also it is a bit computational expensive (O(n) each time one access a character in a string). Still this can be a trick to get back a way to build the CStrings:
typealias CString = Array<CChar>
func toCString(string: String) -> CString {
return Array(string.utf8).map { CChar($0) } + [0]
}
var cString = toCString("$ 0123456789")
println("The 2nd character in cString has value \(cString[1])") // It outputs 32
or without implementing a function:
var container = "$ 0123456789"
var containerAsCString = Array(container.utf8).map { CChar($0) } + [0]
println("The 2nd character in container has value \(containerAsCString[1])") // It outputs 32
Why not just for loop the array and convert everything to Int?
https://developer.apple.com/Library/mac/documentation/General/Reference/SwiftStandardLibraryReference/index.html
Why not just convert the character to String, get the unicodeScalars for it and extract the .value on the scalar?
something like:
var chr: [Character] = ["C", "B", "A"]
for a in String(chr[1]).unicodeScalars {
println(a.value)}
For me worked something like:
"\(container[3])".toInt()

Resources