For the simplicity of my question, this is my Core Data model (doesn't make perfect sense, only for the example):
Book
-------
- title
- readers (to-many relationship to Reader)
Reader
------
- name
- book (to-one relationship to Book)
Currently a book with the same title can have multiple instances in db.
but I want to change that, I want to merge all the books with the same title to one instance (delete all the rest) and merge their readers.
For example, if my db looks like this:
1. Book title "A" readers: "1", "2", "3"
2. Book title "B" readers: "4", "5", "3", "7"
3. Book title "A" readers: "4", "1"
the new db will be:
1. Book title "A" readers: "1", "2", "3", "4"
2. Book title "B" readers: "4", "5", "3", "7"
as you can see, both books with title "A" where merge to one record including the readers.
So my question is how to do this effectively.
I'm thinking of some kind of query that will bring me all the books with more then one instance with the same title, and then maybe order them to groups according to the title.
Not sure if this is the right solution here,
Any help will be appreciated
I do not have enough time for a complete answer, but here is the pointer:
You can fetch duplicates with a fetch request of dictionary result type. You have to group it by title (property description) and add a count column (expression description). You can filter the result with a having argument, so you get the titles with count>1 only.
Hope that helps.
Why are you keeping multiple instances of a book with same title.
Even if two objects have the same contents they are still different.
Why don't you prefer adding readers to the already created book object.
But still if you want to merge the changes something you would want to do is
First fetch all books with the same title like 'A' using a predicate
Than seeing the first object as the one you want to merge in all other objects iterate over all the objects and add its readers but only those with a name that are not already present in the first one.
delete all the book objects with title 'A' except the first one.
Now you would have only one object with a unique Book title having all the readers and the next time you have to add a reader
Fetch the already created book instance from the store.
Add the reader object in it.
UPDATE
Get only the first Book object with a specific title
Then query for all the readers in the store whose book title is the same and set there book object to this one.
Delete all other Book objects with a specific title except the first u used.
Remove multiple occurrences of readers in the book object.
Fetch all the books. Then find the unique titles:
NSArray *uniqueBooks = [allBooks valueForKeyPath:#"#distinctUnionOfObjects.title"];
Then loop over the uniqueBooks and for each book fetch all the matching entities. Convert the entities into an array and do another unique filtering.
NSArray *uniqueReader = [allReaders valueForKeyPath:#"#distinctUnionOfObjects.name"];
Now you have a book and all the readers for that book insert them into a new store. When you are finished building the new store you can use that and delete the old one. You could also insert them into the same store but it would get complicated how to keep the new ones and delete the old ones and when deleting you would have to be careful with the delete behaviours and such.
Related
Say you have a Core Data model with Entity Subjects and Attributes Algebra, Biology, Calculus, Chemistry, Physics, like this:
Subjects
======
Algebra (Boolean)
Biology (Boolean)
Calculus (Boolean)
Chemistry (Boolean)
Physics (Boolean)
Let's say that Algebra, Calculus, and Physics are true while Biology and Chemistry haven't yet been assigned values. How can I get the array: ["Algebra", "Calculus", "Physics"]? I would think it involves NSPredicate but I'm really not sure how to do it.
And as a side question, would default values matter here? i.e., should I make everything start off as false?
Thank you!
You propose an entity named Subjects, with attributes named Algebra, Biology, etc, each of which is a boolean that represents whether the user has "learned" that subject. From that, you wish to compose an array comprising all the names of the subjects that the user has learnt.
Although that is possible, it's quite difficult. I recommend instead defining your Subject entity as:
Subject
=======
name (String)
learnt (boolean)
(Note the convention that entity names are usually singular and start with uppercase, whereas attribute names begin with lowercase.) In the first run of your app you might then create a number of instances of the Subject entity, with the required default values:
name learnt
==== ======
"Algebra" false
"Biology" false
"Calculus" false
... ...
This has the advantage that you can add further subjects at a later date, without needing to modify the structure of your data: it is a simple matter of creating an additional instance with the appropriate name.
As your user progresses through their learning in the app, you can set the value for the learnt attribute to true for the relevant Subject instance. To obtain an array containing only those Subject instances which the user has successfully learnt, you can then use a fetch request with a predicate:
fetchRequest.predicate = NSPredicate(format:"learnt == true")
The array that is returned contains the relevant instances of the Subject entity. It is then simple to obtain the names for display purposes.
You mention in comments having further information about all the subjects. If you model all that in CoreData (eg. having Topic, Lesson, Test entities, etc - I speculate, that's for you to design) you can create relationships to that data from your Subject entity. For example, a Subject might have a relationship to many Topics, each of which has many Lessons and many Tests, etc. That way, when your app starts, you can fetch all the Subject instances and display them in a tableView (showing the name attribute). When the user taps a Subject to begin or continue their learning, you have the relevant Subject instance which provides the link (via the relevant relationship) for you to display the material for the chosen subject.
I'm implementing a search feature in my app. I would like the user to look up a word simultaneously in multiple attributes of a given Entity.
Here is an example for an Entity with 3 String attributes: Person
(firstName, lastName, notes)
Let's use a mock dataset with 3 people:
"Emily", "Bridges", "She will be in town real soon."
"Johnny", "Williams", "This dude is really cool."
"Will", "Smith", "He does not remember anything for some reason."
Now, let's assume the user is looking up the occurence "will" and that we run a case insensitive search. All three previously described people will match the word "will" thanks to the use of an orPredicateWithSubpredicates
Ideally I would like the results to be displayed in this order for relevancy purposes:
"Will", "Smith", "He does not remember anything for some reason."
"Johnny", "Williams", "This dude is really cool."
"Emily", "Bridges", "She will be in town real soon."
For this search feature "firstName" is more relevant than "lastName" which are both more relevant than the "notes" attribute.
Since I'm using a UISearchDisplayController, I also use an NSFetchedResultsController which requires an NSSortDescriptor. The problem for me now is what attribute/key I am going to use to init the NSSortDescriptor?
I've been through many posts already and thought a transient property could help me with this issue, but I can't figure out how/when to set up this transient property which could be named something like "sortKey" and be set to these values:
1: For a match on "firstName"
2: For a match on "lastName"
3: For a match on "notes"
Eventually I guess I could try to run three different requests but then I'd have to give up using NSFetchedResultsController and all its magic...
I don't know whether I'm hitting the limits of NSFetchedResultsController or something but any pointer would be great, thanks!
Joss.
I have a scaffolded Grails application with two domains, Person and Course. Person belongs to Course, and Course hasMany Persons. I have modified show.gsp for Course to list all of the Persons associated with the selected Course.
To achieve this, Course.groovy contains the following line:
List persons = new ArrayList()
And, as a result, the "person" database table contains a persons_idx field. I frequently will be adding new data to the "person" table outside of my Grails application, from an external website.
When INSERTing new data, how to I figure out what to set persons_idx as?
I had originally used a SortedSet instead of an ArrayList for persons, since I care about sorting. But since I am sorting on Person.lastName, and there will always be multiple people with the same last name, then the list will exclude those persons who have the same last names as others. I wish there was another way...
Thanks.
Having two applications manipulate the same Database is a thing to avoid, when possible. Can your 2nd application instead call an action on the controlling app to add a Person to the Course with parameters passed to specify each? That way, only one app is writing to the DB, reducing caching, index, and sequence headaches.
You also state that Person belongsTo Course... so you create a new Person for "Bob Jenkins" for each course that he's in? This seems excessive. You should probably look into a ManyToMany for this.
Without moving to a service, unfortunately, you'd want to change the indices on some if not many of the rows for the children of the Course you're trying to add a Person to, as that index is the sorted index for all the Persons in the Course.
I would suggest going back to a "Set", and do your sorting in the app. Your other question about sorting already told you not to override compareTo to just check the last name. If I were you, I'd forget about overriding compareTo at all (except to check IDs, if you want), and just use the sort() method, passing in a closure that correctly sorts the objects.
I’m very new to iOS development and am having an issue. I have a plist that contains a lot of information for a bunch of different animals. The plist is an array of dictionaries, and each dictionary is an animal. Each dictionary has a key called “type”, which is the type of animal (cat, dog, bird, etc). I want my application to first display a table view that lists all of the possible types. When the user selects the type, they will then go to another table view that lists all of the animals of that selected type. They can then select that specific animal and another view will be displayed that has all the detailed information about that specific animal.
I suppose the best way to approach this is that when a user selects the “type” of animal, a new plist will be generated containing the directory entries from the original plist that have a “type” key that matches what the user selects. How can I implement this? Would this logic be in the viewDidLoad method for the second table view? Once the plist is created that contains only the animals of interest, the cellForRowAtIndexPath method would display the list of animals?
Thanks for any assistance!
David
First: You shouldn't generate any new plists in this situation... If you want to store a list of dictionaries based on a query of your data, it should go into an NSMutableDictionary/NSDictionary. Before creating each table, you should construct one of these dictionaries with the filtered data, and pass it to the table as the data source.
Second: I'd try using a database instead of a plist to make your queries a little easier to manage.
Third: Normalize your data as much as possible, hence, have a database table (or plist if you go that route) of "animal types," and a db table/plist of "animals" ... then reference the animal's type by id, instead of by name. If you change that name in the future, you'll have a hell of a time trying to find every occurrence. In addition, it's less buggy that way; one accidental misspelling will leave you wondering why one animal is causing an error and the others aren't.
I am creating an app that navigates through multiple levels of one-to-many relationships. So for example, pretend that the CoreDataBooks code sample starts with a list of genres, you click on a genre and then get the list of books organized by author as seen in Apple's code sample.
Here is my problem: the Apple documentation tells me I should use a FetchedResultsController to help organize my list of books into sections (among other reasons). But when trying to figure out how to get from "one" genre to my "many" books, the Core Data FAQ tells not to use a fetch. From the FAQ:
I have a to-many relationship from Entity A to Entity B. How do I fetch the instances of Entity B related to a given instance of Entity A?
You don’t. More specifically, there is no need to explicitly fetch the destination instances, you simply invoke the appropriate key-value coding or accessor method on the instance of Entity A.
The problem, of course, is I now have my books in a set, but I want them to get them from a fetched results controller.
What is the best way to proceed here? Should I follow the FAQ, and if so, how do I manage dividing my books up into sections by author?
Or do I use a fetched results controller (which I suspect is better), in which case how do I traverse the one-to-many relationship (since Apple's oh-so-helpful answer is simply "don't")?
Many thanks for your help.
Sasha
You have a data model that looks roughly like this:
Genre{
name:
books<-->>Book.genre
}
Book{
name:
genre<<-->Genre.books
}
In your master table, you run a fetched results controller to get table of Genre objects. Then the user selects one of the row which behind the scenes selects a particular Genre object.
Since every Genre object has a books relationship that points to the related Book objects, you have automatically got a reference to all the related book objects so you don't have to fetch anything. For your book tableview you just create a sorted array of the Book objects in the selected Genre object's books relationship.
Think of a Core Data object graph as a clump of bead strings all woven together in a web or fabric. The beads are objects and the strings are relationships. A fetch plucks one of the bead/objects from the clump but once you have that bead/object in hand, then you can just pull on its string/relationship to pull out all the beads/objects related to the bead in your hand.
So, fetches are used in most cases just to find the starting objects, then you walk relationships to find most of the other objects.
That is why the Apple docs say you don't need a second fetch.