I'm trying to fill a UIPickerView with options using a static variable. Is there a more swifty way in creating a list of metric height instead of a for-loop?
Here's what I got:
static var heightMetric: [(key: String, value: String)] {
var items: (key: String, value: String) = []
for item in 30...330 {
items.append(("\(item)", "\(item) cm"))
}
return items
}
For the imperial form, any idea on a good way to create the list of options in a format as 5' 8" to fill a UIPickerView?
No need for the for-loop actually:
static var heightMetric: [(key: String, value: String)] = {
return (30...330).map {
("\($0)", "\($0) cm")
}
}()
You can even drop the explicit type and simply write static var heightMetric = { ...
Regarding the foot and inch form I do not know of any built-in functionality for that. You either have to calculate the value based on the centimeter value by doing a little bit of math or create a map similar to the above one where you might make use of the % operator to split the number into feet and inches. For example:
static var heightMetricImperial = {
return (12...120).map {
("\($0)", "\($0 / 12)' \($0 % 12)\"")
}
}()
Complete example:
class K {
static var heightMetric = {
(30...330).map {
("\($0)", "\($0) cm")
}
}()
static var heightMetricImperial = {
(12...120).map {
("\($0)", "\($0 / 12)' \($0 % 12)\"")
}
}()
}
print(K.heightMetric)
print(K.heightMetricImperial)
Related
I am trying to access the value from a dictionary using a random number, but I am lost, can someone please guide?
Here is what I have:
var themes = ["Halloween": "πππ€‘π»π€π½", "Sports": "ππβ³οΈβ½οΈπ³π±" , "Faces": "πππ¨π€π€π€€", "Animal": "π¦πΌπΊπΏππ"]
// This Does not work for some reason?
lazy var themeRandomNumber = themes.count.arc4random
lazy var currentTheme = themes[themeRandomNumber]
//Cannot subscript a value of type[String : String]' with an index of type 'Int'
This makes sense since, I am trying to access the key using an Int when it is obviously a String, but not sure how to proceed?
lazy var currentEmoji = themes[currentTheme]
extension Int{
var arc4random: Int{
if self > 0 {
return Int(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -Int(arc4random_uniform(UInt32(abs(self))))
} else {
return 0
}
}
}
Just replace
lazy var currentEmoji = themes[currentTheme]
with
var currentTheme = themes.randomElement()
print(currentTheme?.value) //Optional("ππβ³οΈβ½οΈπ³π±")
print(currentTheme?.key) //Optional("Sports")
Here randomElement is new property which you can use to get random element.
Because you're not accessing the key of your dictionary, you need to select "Halloween", "Sports", "Faces" or "Animal" - your themes dict's keys.
You can use some custom mapping method with Int.random(in: 0...3) or a Keys enum conforming to CaseIterable, and then you need to select a random character (emoji) in the String for your given Key (via a random number in the range 0..<string.length).
EDIT
With Swift 4.2+ (Xcode 10) you can use randomElement():
var themes = ["Halloween": "πππ€‘π»π€π½", "Sports": "ππβ³οΈβ½οΈπ³π±" , "Faces": "πππ¨π€π€π€€", "Animal": "π¦πΌπΊπΏππ"]
var randomEmoji = themes.randomElement()?.value.randomElement()
I've got a question on property observers. There's some example code below. What I want is for the property Analysis.hasChanged to be updated to true if a.value is changed. Is there a way I can do this?
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
You can use the built-in property observers provided by Swift.
Every time you set a new value, the didSet will be called. You just need to attach the closure, wrapping the desired behaviour, to the Number class
class Number {
var valueDidChangeClosure: (()->())?
var value: Double {
didSet {
//won't call the valueDidChangeClosure
//if the value was changed from 10 to 10 for example..
if oldValue != value {
valueDidChangeClosure?()
}
}
}
init(numberValue: Double) {
self.value = numberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
self.a.valueDidChangeClosure = {
self.hasChanged = true
}
}
}
let testNumber = Number(numberValue: 4)
let testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will print "true"
I would do something like this, I apologize in advance if I have some syntax wrong (I usually use C/C++, think of this as more psudo code since you'd have to have a way to copy Number classes, etc.).
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var _a: Number
bool hasChanged() {
if (a != _a) {
_a = a
return true;
}
return false;
}
init(inputNumber: Number) {
self.a = inputNumber
self._a = self.a
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged()) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged()) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
I don't know if this really addresses that question, I based my answer off of the code you provided. So there may be additional functionality if you want there to be some triggering method (instead of calling .hasChanged()).
Comparing doubles (and any other floating point type) with '=' or '!=' is not a good idea.
Use epsilon function instead.
Details: jessesquires.com/blog/floating-point-swift-ulp-and-epsilon/
I am trying to use a list that is a value for a dictionary key/pair set, and this dictionary is itself a value in a key/pair set in a dictionary. To explain, this is how I initialize it.
var dictOfEvents = [Int: [Int: [PFObject]]]()
I am trying to add events to the list, with the inner dictionary's key being the day of month and the outer one being the month. For example, an event on May 1 would be:
dictOfEvents[5:[1:[ListOfEvents]]
Where ListOfEvents is an array of PFObjects. Before I added the month functionality, and thus the outer dictionary, the way I added new events was:
` self.dictOfEvents[components.day] = [event]
But now, when I try to extend this with:
self.dictOfEvents[components.month]?[components.day]! = [event]
It does not work. Any explanation on how to create new event lists and access this double layer dictionary would be greatly appreciated.
(Note: I don't know where to put the ! and the ? in the last piece of code so please excuse me if I made a mistake.)
Here is what I think could be a good use of optionals in your case (and should respond to your question):
var dic: [Int: [Int: [String]]] = [:]
dic[5] = [1:["Hello", "World"]]
if let list = dic[5]?[1] {
// your list exist and you can safely use it
for item in list {
println(item)
}
}
I just used String instead of PFObject.
A different approach could be:
/*
Define a struct to encapsulate your Month and Day
Make it Hashable so that you can use it as Dictionary key
*/
public struct MonthDay: Hashable {
let month: Int
let day: Int
public var hashValue: Int { return month * 100 + day }
}
public func ==(lhs: MonthDay, rhs: MonthDay) -> Bool {
return lhs.month == rhs.month && lhs.day == rhs.day
}
var dictOfEvents = [MonthDay :[String]]()
let aMonthAndDay = MonthDay(month: 5, day: 1)
dictOfEvents[aMonthAndDay] = ["Hello", "World"]
if let list = dictOfEvents[aMonthAndDay] {
// your list exist and you can safely use it
for item in list {
println(item)
}
}
U can simple change:
self.dictOfEvents[components.month]?[components.day]! = [event]
to :
self.dictOfEvents[components.month]![components.day]! = [event]
Because Dictionary has subscript, Dictionary? doesn't have subscript.
if U try add Events to Dictionary. I suggest to use this:
var dictOfEvents = [Int: [Int: [PFObject]]]()
var dictOfDayEvents = [Int:[PFObject]]()
dictOfDayEvents.updateValue([PFObject()], forKey: 1)
dictOfEvents.updateValue(dictOfDayEvents, forKey: 5)
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 = ""
}
}
I have these two objects in my model:
Message:
class Message: Object {
//Precise UNIX time the message was sent
dynamic var sentTime: NSTimeInterval = NSDate().timeIntervalSince1970
let images = List<Image>()
}
Image:
class Image: Object {
dynamic var mediaURL: String = ""
var messageContainingImage: Message {
return linkingObjects(Message.self, forProperty: "images")[0]
}
}
I want to form a query which returns messages and images, messages sorted by sentTime and images sorted by their messageContainingImage's sent time. They'd be sorted together.
The recommended code for a query is this:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
This returns a Result<Message> object. A Result doesn't have a way to be joined to another Result. There are other issues in my way too, such as, if I could combine them, how would I then perform a sort.
Additional thoughts:
I could also add a property to Image called sentTime, then once they're combined I'd be able to call that property on both of them.
I could make them both subclass from a type which has sentTime. The problem is, doing Realm().objects(Message) would only returns things which are messages, and not subclasses of Message.
How would I be able to do this?
My end goal is to display these message and image results in a tableview, messages separately from their attached image.
I think, inheritance is not the right solution here, this introduces more drawbacks by complicating your object schema, than it's worth for your use case.
Let's go back to what you wrote is your end goal: I guess you want to display messages and images together in one table view as separated rows, where the images follow their message. Do I understand that correctly?
You don't need to sort both, sorting the messages and accessing them and their images in a suitable way will ensure that everything is sorted correctly. The main challenge is more how to enumerate / random-access this two-dimensional data structure as an one-dimensional sequence.
Depending on the amount of data, you query, you have to decide, whether you can go a simple approach by keeping them all in memory at once, or introducing a view object on top of Results, which takes care of accessing all objects in order.
The first solution could just look like this:
let messages = Realm().objects(Message).sorted("sentTime", ascending: true)
array = reduce(messages, [Object]()) { (var result, message) in
result.append(message)
result += map(message.images) { $0 }
return result
}
While the latter solution is more complex, but could look like this:
// Let you iterate a list of nodes with their related objects as:
// [a<list: [a1, a2]>, b<list: [b1, b2, b3]>]
// in pre-order like:
// [a, a1, a2, b, b1, b2, b3]
// where listAccessor returns the related objects of a node, e.g.
// listAccessor(a) = [a1, a2]
//
// Usage:
// class Message: Object {
// dynamic var sentTime = NSDate()
// let images = List<Image>()
// }
//
// class Image: Object {
// β¦
// }
//
// FlattenedResultsView(Realm().objects(Message).sorted("sentTime"), listAccessor: { $0.images })
//
class FlattenedResultsView<T: Object, E: Object> : CollectionType {
typealias Index = Int
typealias Element = Object
let array: Results<T>
let listAccessor: (T) -> (List<E>)
var indexTransformVectors: [(Int, Int?)]
var notificationToken: NotificationToken? = nil
init(_ array: Results<T>, listAccessor: T -> List<E>) {
self.array = array
self.listAccessor = listAccessor
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
self.notificationToken = Realm().addNotificationBlock { note, realm in
self.recomputeTransformVectors()
}
}
func recomputeTransformVectors() {
self.indexTransformVectors = FlattenedResultsView.computeTransformVectors(array, listAccessor)
}
static func computeTransformVectors(array: Results<T>, _ listAccessor: T -> List<E>) -> [(Int, Int?)] {
let initial = (endIndex: 0, array: [(Int, Int?)]())
return reduce(array, initial) { (result, element) in
var array = result.array
let list = listAccessor(element)
let vector: (Int, Int?) = (result.endIndex, nil)
array.append(vector)
for i in 0..<list.count {
let vector = (result.endIndex, Optional(i))
array.append(vector)
}
return (endIndex: result.endIndex + 1, array: array)
}.array
}
var startIndex: Index {
return indexTransformVectors.startIndex
}
var endIndex: Index {
return indexTransformVectors.endIndex
}
var count: Int {
return indexTransformVectors.count
}
subscript (position: Index) -> Object {
let vector = indexTransformVectors[position]
switch vector {
case (let i, .None):
return array[i]
case (let i, .Some(let j)):
return listAccessor(array[i])[j]
}
}
func generate() -> GeneratorOf<Object> {
var arrayGenerator = self.array.generate()
var lastObject: T? = arrayGenerator.next()
var listGenerator: GeneratorOf<E>? = nil
return GeneratorOf<Object> {
if listGenerator != nil {
let current = listGenerator!.next()
if current != nil {
return current
} else {
// Clear the listGenerator to jump back on next() to the first branch
listGenerator = nil
}
}
if let currentObject = lastObject {
// Get the list of the currentObject and advance the lastObject already, next
// time we're here the listGenerator went out of next elements and we check
// first whether there is anything on first level and start over again.
listGenerator = self.listAccessor(currentObject).generate()
lastObject = arrayGenerator.next()
return currentObject
} else {
return nil
}
}
}
}