I am new to Realm and having problem with query that drive me crazy for several days. I have 2 simple classes Dog and Person as:
class Dog: Object {
dynamic var dogName = ""
dynamic var dogAge = 1
var owners: [Person] {
return linkingObjects(Person.self, forProperty: "dogs")
}
}
class Person: Object {
dynamic var firstName = ""
let dogs = List<Dog>()
}
After adding data to Realm, I have the following data:
Oscar have 3 dogs namely A, B and C with respected age of 4, 6, and 9
Jennifer have 3 dogs namely D,E and F with respected age of 1, 2 and 7
One dog named Z, age 8 and have no owner.
let database = try! Realm()
// Query all Dogs- It should have 7 dogs
let allDogs = database.objects(Dog)
print("A total of \(allDogs.count) dogs") // 7 dogs - correct
// Query All dogs between age of 4 and 12 - there should be 5 dogs
let agedDogs = database.objects(Dog).filter("dogAge BETWEEN {4,12}")
print("Dogs between Age 4 and 12 = \(agedDogs.count) \n") // 5 dogs - OK
I want to know of all those agedDog, how many of them belong to Oscar?
I tried:
let specificOwnerAgedDogs = agedDogs.filter("ANY owners.firstName = 'Oscar'")
print("Oscar Dogs between Age 4 and 12 = \(specificOwnerAgedDogs)")
But this query results in an error:
Terminating app due to uncaught exception 'Invalid property name', reason: 'Property 'owners' not found in object of type 'Dog''
I am using the Realm Swift with Swift 2.1.1.
Realm Swift does not yet support provide direct support for queries that traverse inverse relationships, such as the owners property on your Dog class. This is something we're working on, and that should make it into a Realm Swift release in the near future.
For now, you can work around this limitation by performing some of the filtering in Swift rather than in the predicate:
let specificOwners = database.objects(Person).filter("firstName = 'Oscar'")
let specificOwnerAgedDogs = specificOwners.flatMap() { owner in
owner.dogs.filter() { $0.dogAge >= 4 && $0.dogAge <= 12 }
}
let noOwn = agedDogs.flatMap {
(ndog) -> Dog? in
ndog.owners == [] ? ndog : nil
}
I use this code to find out the list of all agedDogs that do not any owner?
Any thought on this?
Related
I have this structure: [String: [String: Double]]()
Specifically, something like that: var dictionaries = ["GF": ["ET": 4.62, "EO": 21.0],"FD": ["EE": 80.95, "DE": 0.4]]
How can I easily access and modify nested dictionaries?
EXAMPLE UPDATED: I want to append "TT": 6 at FD and later I want to append another dictionary inside the array. At the end I'll print the results.
for (key,value) in dictionaries {
// if array contains FD, add the record to FD
if key.contains("FD") {
dictionaries["FD"]!["TT"] = 6
}
else {
// if array doesn't contain FD, add FD and add the record to it
dictionaries = dictionaries+["FD"]["TT"] = 6 // <-- I know that it's wrong but I want to achieve this result in this case.
}
}
Result of print will be:
GF -> ET - 4.62, EO - 21.0
FD -> EE - 80.95, DE - 0.4, TT - 6
MISSION: I need to append new dictionary records like in the example above, update existing ones in a simple and straightforward way, loop easily through records to read the values and print them out.
Can anyone help me? Never had the chance to manage dictionaries in Swift since now.
It is not clear what exactly you need but the exercise amused me, so I came up with this solution: we extend Dictionary so that it provides convenience methods if it is a nested dictionary.
First, because of Swift idiosyncrasies, we have to create a dummy protocol to "mark" Dictionary¹:
protocol DictionaryProtocol {
associatedtype Key: Hashable
associatedtype Value
subscript(key: Key) -> Value? { get set }
var keys: LazyMapCollection<[Key : Value], Key> { get }
}
extension Dictionary: DictionaryProtocol {}
Basically, just copy-paste² all declarations you need later from Dictionary to DictionaryProtocol.
Then, you can happily extend away. For instance, add a two-parameter subscript:
extension Dictionary where Value: DictionaryProtocol {
typealias K1 = Key
typealias K2 = Value.Key
typealias V = Value.Value
subscript(k1: K1, k2: K2) -> V? {
get {
return self[k1]?[k2]
}
set {
if self[k1] == nil {
self.updateValue([K2: V]() as! Value, forKey: k1)
}
self[k1]![k2] = newValue
}
}
}
Or an alternative pretty-print³:
extension Dictionary where Value: DictionaryProtocol {
func pretty() -> String {
return self.keys.map { k1 in
let row = self[k1]!.keys.map { k2 in
return "\(k2) - \(self[k1]![k2]!)"
}.joined(separator: ", ")
return "\(k1) -> \(row)"
}.joined(separator: "\n")
}
}
You can also create a type alias for this special dictionary:
typealias D2Dictionary<K: Hashable, V> = Dictionary<K, Dictionary<K, V>>
Going back to the example in your question:
var dictionary = D2Dictionary<String, Double>()
dictionary["GF", "ET"] = 4.62
dictionary["GF", "EO"] = 21.0
dictionary["FD", "EE"] = 80.95
dictionary["FD", "DE"] = 0.4
dictionary["FD", "TT"] = 6
print(dictionary.pretty())
// > GF -> ET - 4.62, EO - 21.0
// > FD -> EE - 80.95, DE - 0.4, TT - 6.0
Background: Only protocols can be used in type bounds on extension conditions.
Make sure to get the types right. If we write var keys: [Key] { get }, for instance, the compiler dies with a seg fault.
Unfortunately, extension Dictionary: CustomStringConvertible where Value: DictionaryProtocol { ... } is not allowed, for whatever reason.
It'll be much simpler if you have the key "FD" you can use
dictionaries["FD"]!["TT"] = 6
to add a new value ["TT":6] or modify existing value of TT to 6. Note that the ! between ["FD"]!["TT"] assumes that ["FD"] exists regardless. You need to check if ["FD"] exists otherwise. Like:
if dictionaries["FD"] != nil {
dictionaries["FD"]!["TT"] = 6
} else {
dictionaries["FD"] = ["TT" : 6]
}
If you need to look for the key you will have to run the entire dictionary like you already tried, but dictionaries support fast enumeration for key and values like
for (key,value) in dictionaries {
if key.contains("FD") { //or any other checks you need to identify your key
value["TT"] = 6
}
}
I have an array of custom objects and want to know how to identify which objects are duplicates. I know how to remove the duplicates but that is not functionality I'm after. Im using swift 2.
Example:
var movies: [Movie] = ["Batman v Superman: Dawn of Justice", "Batman v Superman: Dawn of Justice", "Deadpool"," "Deadpool", "Hardcore Henry", "Jason Bourne", "Jurassic World"]
So i want to show a table view with the list of movies above but with "Batman" and "Deadpool" highlighted.
For more clarity on what I'm trying to achieve look at the screenshot. I have a list of movies that two users select in a previous view controller. I want to display the selected movies in a table view. I want to specifically show if there were movies that both people selected.
Based on your comment I have made a simple example with string arrays, which can easily be converted to your movie type:
let movies = ["Batman","Batman","Flash","Avengers"]
var movieCounts:[String:Int] = [:]
for movie in movies {
movieCounts[movie] = (movieCounts[movie] ?? 0) + 1
}
And you can test it like so:
for (key, value) in movieCounts {
print("\(key) has been selected \(value) time/s")
}
I generally try to avoid posting answers with code entirely written by someone else (MIT License), but the reference below is such a neat fit for this question that I believe it's worth including as an answer.
The solution use the same technique as the accepted answer, but on a more general form (made compact with a neat subscript extension to Dictionary): the freq() dictionary extension from GitHub user oisdk's excellent SwiftSequence framework (MIT License):
/* ---------------------------------------------------------------------------
source: GitHub user oisdk:
https://github.com/oisdk/SwiftSequence/blob/master/Sources/Categorise.swift */
private extension Dictionary {
subscript(key: Key, or or: Value) -> Value {
get { return self[key] ?? or }
set { self[key] = newValue }
}
}
public extension SequenceType where Generator.Element : Hashable {
// MARK: Frequencies
/**
Returns a dictionary where the keys are the elements of self, and
the values are their respective frequencies
```swift
[0, 3, 0, 1, 1, 3, 2, 3, 1, 0].freqs()
// [2: 1, 0: 3, 3: 3, 1: 3]
```
*/
#warn_unused_result
func freqs() -> [Generator.Element:Int] {
var freqs: [Generator.Element:Int] = [:]
for el in self { freqs[el, or: 0] += 1 }
return freqs
}
}
/* --------------------------------------------------------------------------- */
/* example usage */
let movies = ["Batman","Batman","Flash","Avengers"]
print(movies.freqs()) // ["Avengers": 1, "Flash": 1, "Batman": 2]
Have a look at the framework for lots of other sequence goodies:
https://github.com/oisdk/SwiftSequence
Why not adding an id in the Movie object
and compare the two arrays searching the same object.
public class Movie:Equatable{
var id=NSUUID().UUIDString
}
public func ==(lhs: Movie, rhs: Movie) -> Bool{
return lhs.id == rhs.id
}
Comparing the arrays:
var moviesA=[Movie]()
var moviesB=[Movie]()
var sharedMovies=[Movie]()
for movie in moviesA{
if moviesB.contains(movie){
sharedMovies.append(movie)
}
}
Not sure what functionalities you want to make.
If just for list of items, you can use swift dictionary to count duplicates, by using movie name as key and count as value starting by 0.
If you want to highlight, you can use different style when you populate the table in the delegate method by checking if the item has duplicates.
Thanks for taking the time to read my question.
I have experience in php with multidimensional arrays but i am struggling to transfer my knowledge over to swift...
The plan is to create an array like this:
Category1
-- product1
--- description
-- product2
--- description
Category2
-- product1
--- description
-- product2
--- description
Category3
-- product1
--- description
-- product2
--- description
And using 3 tableviews (1 on each viewcontroller) i would like to populate the info:
Tableview1 -- Category list.
Tableview2 -- Product for selected category list.
Tableview3 -- Description for selected.
I can populate 1 tableview using a simple array easily, but how would i:
1) Create the multidimensional array?
2) Count & populate each tableview?
3) Tie each child row to parent rows?
I really hope i am making sense and i am not looking for a free ride, just a helping hand as i am really enjoying learning the swift language!
Kind regards
Rory
Welcome to swift user1263909
How to create an array of strings
let stringArray = [String]()
How to create an array of strings with string literals
let categoryStringArray = ["Category1", "Catagory2", "Catagory3"]
How to create an array of string array's
let arrayOfArray = [[String]]()
how to create an array of string array literals
let a = [["Category1", "Catagory2", "Catagory3"],["product1", "product2", "product3"]]
accessing "Catagory3"
a[0][2]
However this all can get complicated fast. Why not use some structs?
struct Product {
var name:String
var description:String
var price:Double
//ect...
}
use struct to encapsulate data for easy use
let p1 = Product(name: "hat", description: "a hat", price: 2.00)
lets make an array of structs.
let arrOfProduct = [p1,Product(name: "tie", description: "a bow tie", price: 5.00)]
arrOfProduct now has two structs in it a tie and a hat.
How to access hat's price
arrOfProduct[0].price
oh we could also have a struct for catagories, it can contain products as an array
struct Category {
var name:String
var products:[Product]
}
Lets create a Category for Clothes.
let clothes = Category(name: "Clothing", products: arrOfProduct)
lets create another catagory
let food = Category(name: "Food", products: [Product(name: "Candy", description: "Yummy Candy", price: 2.0)])
and finally we can have an array of Catagory
let arrC = [clothes,food]
now I can loop through all catagories in first table view to fill out the cell names
for c in arrC{
print(c.name)
}
when you switch to a new view controller pass it the correct array of products
let productsClicked = arrC[0].products
then on that new view controller loop the products
for p in productsClicked{
print(p.name)
}
if they click a product pass the product to the next view controller and so on.
this will be much easier to maintain as you get new requirments.
Goodluck and welcome to swift. :D
I am not expert in Swift and I have been using it for few months to build Mac Apps. I would like to represent in memory a data structure like that of PHP associative arrays but in Swift. Let's imagine that I have a table of data to load in memory with the following fields/records:
ID Surname Name
1 XXX YYY
2 ZZZ WWW
3 JJJ KKK
What I would like to obtain is an associative array like the one I would be able to obtain in PHP:
$arr[1]["Surname"] = "XXX"
$arr[1]["Name"] = "YYY"
$arr[2]["Surname"] = "ZZZ"
$arr[2]["Name"] = "WWW"
I just cannot find the right data structure in Swift to obtain the same result. I tried with the following piece of code:
class resObject: NSObject {
private var cvs = [Int: [String: String]]()
override init() {
self.cvs[0] = ["Name" : "XXX"]
self.cvs[0] = ["Surname" : "YYY"]
self.cvs[1] = ["Name" : "ZZZ"]
self.cvs[1] = ["Surname" : "WWW"]
for (key, arr) in cvs {
let sur = arr["Surname"]
let nam = arr["Name"]
println("Row \(key) - Surname: \(sur), Name: \(nam)")
}
super.init()
}
}
It looks to me pretty close, but it does not work. What I get in the output is the following (I don't care about the "Optional(s)":
Row 0 - Surname: Optional("XXX"), Name: nil
Row 1 - Surname: Optional("ZZZ"), Name: nil
I tried to make some tests in debug and I noticed that the data that are saved in memory are just that of the last key:value pair used (i.e. if I assign Surname first and Name second I get Surname as nil and Name with the correct value).
Please consider that, as in the example, I don't know the data structure when I declare the variable, so I declare it empty and fill it programmatically later.
I don't know if it is just me not declaring the data structure correctly, or if it is Swift that does not allow to do that. Any help will be greatly appreciated.
Thanks a lot.
Regards,
Alessio
One way is a Dictionary of structs. Consider:
struct Person {
var firstName: String
var lastName: String
}
var peopleByID = [ Int: Person ]()
peopleByID[1] = Person(firstName: "First", lastName: "Last")
peopleByID[27] = Person(firstName: "Another", lastName: "LastName")
var myID = 1 // Try changing this to 2 later
if let p = peopleByID[myID] {
println("Found: \(p.firstName) with ID: \(myID)")
}
else {
println("No one found with ID: \(myID)")
}
You can then update the structure:
peopleByID[1].firstName = "XXX"
peopleByID[27].lastName = "ZZZ"
You can iterate freely:
for p in peopleByID.keys {
println("Key: \(p) value: \(peopleByID[p]!.firstName)")
}
Note that a mere array of [Person] isn't so hot, because the IDs:
-- may not be Ints, but are often Strings
-- even if they remain Ints, an array takes up storage in proportion to the highest numbered index, whereas a Dictionary only takes up storage in proportion to the number of stored objects. Imagine storing just two ID's: 523123, and 2467411.
EDIT
It seems like you don't know the attributes ahead of time that will go into each Person object. That's odd, but you should then do:
struct Person {
var attributes = [String : String]() // A dictionary of String keys and String values
}
var peopleByID = [ Int : Person ]()
// and then:
var p1 = Person()
var p2 = Person()
p1.attributes["Surname"] = "Somename"
p1.attributes["Name"] = "Firstname"
p2.attributes["Address"] = "123 Main St."
peopleByID[1] = p1
peopleByID[2] = p2
if let person1 = peopleByID[1] {
println(person1.attributes["Surname"]!)
for attrKey in person1.attributes.keys {
println("Key: \(attrKey) value: \(person1.attributes[attrKey]!)")
}
}
I'm working on a SRS-based Flashcard program in swift. My problem (other than being new at programming, haha) is... once I alter the values of due date, score, etc; how do I add them back to the wordBank array keeping the new property values?
I started out creating a class for other flashcards to inherit properties like word, translation, sentence 1 & 2, audio URL, score (1-100 for scheduling purposes), etc; etc;
class Flashcard {
var id: Int = 0
var score: Int = 0
var word: String = ""
var sentence1: String = ""
// etc; etc;
From here, I created a new file and class called words, inheriting the Flashcard properties.
class Words: Flashcard {
// Example word 1
let you = Flashcard()
you.id = 0
you.score = 2
you.word = "khun"
you.sentence1 = "khun blah blah"
// Example word 2
let me = Flashcard()
you.id = 1
you.score = 1
you.word = "pohm"
you.translation = "Me or I"
// Create an array of all the words that will be in the program
var wordBank: [Flashcard] = [you, me, him, her, them, it] // etc;
Now, from here I can sort
// Sort array by lowest
wordBank.sort({ $1.score > $0.score })
// Create a new array for testing Session
var SessionArray: [Flashcard] = []
// Add the lowest scores to the SessionArray for testing
SessionArray += WordArray[0...6]
For the sake of remembering, My problem is once I alter the values of due date, score, etc; how do I add them back to the wordBank array keeping the new property values? I won't bother asking about how to persist this data right now, lol. I'm still reading about core data.
Thanks for any help you can give me!