How can i take this array and make into one array? - ios

PFQuery *query = [PFQuery queryWithClassName:#"Category"];
// Retrieve the object by id
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
// NSLog(#"%#",[objects objectAtIndex:1]);
// NSLog(#"%#",[objects valueForKey:#"mensClothingType"]);
NSArray * result = [[objects valueForKey:#"mensClothingType"] componentsJoinedByString:#""];
NSLog(#"%#",result);
}];
}
how can i get all of the 'mensClothingType' from objects and put that into one array? currently if i read out the array 'objects'
i get this:
(
(
Shirts
),
(
Jeans
)
)

NSMutableArray* resultArray = [NSMutableArray new];
for(NSArray* innerArray in outerArray)
{
for(id object in innerArray)
{
[resultArray addObject:object];
}
}

Related

Best way to fetch child and parent objects with Parse Server in iOS

I'm trying out the "Sample Blog App" on Parse Server for iOS and cannot figure out what is the smartes way to fetch all child objects of another class (together with the parent objects).
The "Sample Blog App" (which creates automatically when you create a new account) contains the classes Comment and Post. The Comment class contains a relation to the Post class as shown below (from the dashboard), but there is no relation in the opposite direction.
Now, I want to fetch all posts and all the comments related to each post. The code below does that, but I'm assuming there must be a smarter way...? If you know how, please share. Thanks in advance!
- (void)fetchPosts {
NSString *commentsKey = #"comments";
NSString *postKey = #"post";
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error == nil) {
NSMutableArray *array = [[NSMutableArray alloc]init];
for (PFObject *comment in objects) {
PFObject *post = [comment objectForKey:postKey];
NSDictionary *existingPostDict = [[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K = %#", #"post.objectId", post.objectId]] firstObject];
if (existingPostDict) {
// update comments
NSArray *comments = [[existingPostDict objectForKey:commentsKey] arrayByAddingObject:comment];
// create new dictionary and replace the old one
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:[existingPostDict objectForKey:postKey], postKey, comments, commentsKey, nil];
[array replaceObjectAtIndex:[array indexOfObject:existingPostDict] withObject:newPostDict];
}
else {
// first post, create a new dict
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:post, postKey, #[comment], commentsKey, nil];
[array addObject:newPostDict];
}
}
self.posts = array; // assuming: #property (nonatomic, strong) NSArray *posts;
}
else {
NSLog(#"Error fetching posts: %#", error.localizedDescription);
}
}];
}
Instead of using include on your query you should use whereKey:equals: and pass the post object as the second argument. This will filter and return only the comment objects that contain that have that post as their value for post
One problem I see with your query is that there is a possibility this will not fetch every post in the database. If a post has 0 comments, none of the Comment objects will have a reference to it and thus you will not receive it.
Therefore you should actually do a query on "Post" and in its completion do a query on "Comment". This way you will not miss any posts with 0 comments. When you do this, you will not need to include the "post" key in the Comment query. This has multiple benefits.
First, each include is also another query for that object. So each new Comment object will create another query in the backend. You will get rid of this automatically.
Second, for a "Post" with multiple comments, you will be querying for the same post multiple times and that same post will be returned multiple times which consumes unnecessary bandwidth.
After getting Posts and Comments separately just combine them.
Apart from that I would do the combining like so which I find more readable but that is just personal preference.
- (void)fetchPosts {
NSString *commentsKey = #"comments";
NSString *postKey = #"post";
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error == nil) {
NSMutableArray *array = [[NSMutableArray alloc]init];
NSMutableDictionary *d = [NSMutableDictionary dictionary];
for (PFObject *comment in objects) {
PFObject *post = [comment objectForKey:postKey];
if (d[post.objectId]) {
[d[post.objectId][commentsKey] addObject:comment];
}
else{
d[post.objectId] = [NSMutableDictionary dictionary];
d[post.objectId][postKey]=post;
d[post.objectId][commentsKey] = [NSMutableArray arrayWithObject:comment];
}
}
for (NSString *key in [d allKeys]) {
[array addObject:d[key]];
}
self.posts = array; // assuming: #property (nonatomic, strong) NSArray *posts;
}
else {
NSLog(#"Error fetching posts: %#", error.localizedDescription);
}
}];
}
This is how I did it, using findObjectsInBackground together with continueWithSuccessBlock: methods (for better error handling one can choose continueWithBlock: instead):
- (void)fetchPosts {
/**
create "post" and "comment" queries and use a BFTask-method from
Bolts.framework to chain downloading tasks together (bundled with Parse SDK)
*/
NSMutableArray *posts = [NSMutableArray new];
PFQuery *postQuery = [PFQuery queryWithClassName:#"Post"];
[[[postQuery findObjectsInBackground] continueWithSuccessBlock:^id(BFTask * task) {
[posts addObjectsFromArray:task.result];
PFQuery *commentsQuery = [PFQuery queryWithClassName:#"Comment"];
return [commentsQuery findObjectsInBackground];
}] continueWithSuccessBlock:^id(BFTask * task) {
/**
loop through posts and filter out comments with the same objectId in post,
then create a dictionary with post and related comments. done! :)
*/
NSMutableArray *postsAndComments = [NSMutableArray new];
for (PFObject *post in posts) {
NSArray *comments = [task.result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"post.objectId", post.objectId]];
[postsAndComments addObject:#{#"post":post, #"comments":comments}];
}
/**
note: BFTask-blocks not running in main thread!
*/
dispatch_async(dispatch_get_main_queue(), ^{
self.posts = postsAndComments; // assuming: #property (nonatomic, strong) NSArray *posts;
});
return nil;
}];
}

BFTask to return when nested query is complete

I'm performing a query in conjunction with BFTask and Parse. Within the task is a nested query. I wish to return the task only when the outer query has finished fetching the necessary data to add to complete (including data fetched by the inner query). I have already achieved a solution using the main thread, however I do not want to block the user interface.
+ (BFTask*)theTask {
BFTask *task = [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{
NSMutableArray *complete = [NSMutableArray new]; //do not return complete until it has been populated by relationObj's
[[QueryLibrary queryA] findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
for(PFObject *obj in objects) {
PFQuery *relationQuery = [obj relationForKey:#"aRelation"].query;
[relationQuery findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
for(PFObject *relationObj in objects) {
//inspect and possibly augment relationObj...
[complete addObject: relationObj];
}
}];
}
}];
return complete;
}];
return task;
}
I've tried to restructure my query (queryA) to include the relation. When trying to include the relation, I get the following errors:
+ (PFQuery *)queryA {
PFQuery *query = [PFQuery queryWithClassName:#"aPFObjectSubclass"];
//include other qualifiers...
[query includeKey:#"aRelation"]; //[Error]: field aRelation cannot be included because it is not a pointer to another object (Code: 102, Version: 1.11.0)
[query whereKeyExists:#"aRelation"]; // [Error]: Unsupported query operator on relation field: aRelation (Code: 102, Version: 1.11.0)
return query;
}
Is it possible in your scenario to call [query includeKey:'aRelation']; on the first query?
That would make it return the aRelation objects without having to run relationQuery in a loop, which would eliminate the problem of doing that on the main thread altogether.
More info: http://blog.parse.com/announcements/queries-for-relational-data/
In .h file declare typedef void(^ completion)(NSMutableArray *);
Implementation of your method should look like this:
+ (BFTask *)theTask:(completion) completion {
BFTask *task = [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{
NSMutableArray *complete = [NSMutableArray new]; //do not return complete until it has been populated by relationObj's
[[QueryLibrary queryA] findObjectsInBackgroundWithBlock:^(NSArray * _Nullable topObjects, NSError * _Nullable error) {
for(PFObject *obj in topObjects) {
PFQuery *relationQuery = [obj relationForKey:#"aRelation"].query;
[relationQuery findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
for(PFObject *relationObj in objects) {
//inspect and possibly augment relationObj...
[complete addObject: relationObj];
}
if([topObjects lastObject] == obj) {
completion(complete);
}
}];
}
}];
}];
return task;
}
Calling your method:
[myClass theTask:^(NSMutableArray * complete) {
//use your "complete" array
}];
I wonder if this would work:
+ (BFTask *)theTask
{
BFTask *task = [BFTask taskFromExecutor:[BFExecutor executorWithOperationQueue:[NSOperationQueue defaultQueue]] withBlock:^id {
NSArray *objectsWithRelations = [[QueryLibrary queryA] findObjects];
NSMutableArray *subqueries = [NSMutableArray array];
for(PFObject *object in objectsWithRelations) {
[subqueries addObjects:[obj relationForKey:#"aRelation"].query];
}
PFQuery *query = [PFQuery orQueryWithSubqueries:subqueries];
NSArray *complete = [query findObjects];
return complete;
}];
return task;
}
There is a chance that the findObject calls will be synchronous on the thread that the BFTask is dispatched onto, and not dispatched onto the main thread.
In any case, you can reduce the number of finds you do by merging the relationship queries into one OR query.
Update:
Would this allow you to run only part of the task synchronously then:
+ (BFTask *)theTask
{
NSArray *objectsWithRelations = [[QueryLibrary queryA] findObjects];
NSMutableArray *subqueries = [NSMutableArray array];
for(PFObject *object in objectsWithRelations) {
[subqueries addObjects:[obj relationForKey:#"aRelation"].query];
}
PFQuery *query = [PFQuery orQueryWithSubqueries:subqueries];
return [query findObjectsInBackground];
}

Split nsarray to nsdictionary

I am quering Parse and get an array back
PFQuery *query = [Points query];
[query whereKey:#"city" equalTo:[SharedParseStore sharedStore].chosenCity];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
I want to sort the array into a dictionary based on the district value that is in the Point object so that i can use a tableview with sections (district section names)
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
for (Points *point in objects) {
[dict setValue:point forKey:point.district];
}
block(dict, error);
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
The problem is it only adds 1 value of course
So to make it easy to understand:
The objects coming from Parse are Point objects with the following property's:
Name,District,City
I want to create a NSdictionary with the districts (so I need to collect them out of the objects first because I don't know them) as a key and the value for that key is an array with the points that are in that district.
I don't know beforehand what the districts will be. They will need to be picked from the objects array that is returned from Parse.
My final object I want to create is a nsdictionary with arrays of points for every distinct district(that is the key).
Example:
[#"districtA" : an array with Point objects that have districtA in their district property, etc etc]
what is the best way to do this because i really can't see how to do it ?
You answered your own question when you say:
I want to create a NSdictionary with the districts as a key and the
value for that key is an array with the points that are in that
district.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableDictionary *dict = [#{} mutableCopy];
for (Points *point in objects) {
// if no point for that district was added before,
// initiate a new array to store them
if (![dict containsKey:point.district])
dict[point.district] = [#[] mutableCopy];
[dict[point.district] addObject:point];
}
block(dict, error);
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];

Get Array With Array but it's goes array in array

I have data on database like this.
["{37.331622, -122.030337}","{37.331593, -122.03051}","{37.331554, -122.030681}","{37.331383, -122.030757}","{37.33108, -122.030772}","{37.330798, -122.030729}","{37.330636, -122.030636}"]
Then i try to query data from database by following code.
- (void)updateLocations {
CGFloat kilometers = self.radius/1000.0f;
//PFUser *user = [PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"Session"];
[query whereKey:#"objectId" equalTo:#"t2udAri048"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"objects %#",objects);
NSLog(#"path %#",[objects valueForKey:#"Path"]);
NSArray *pointsArray = [objects valueForKey:#"Path"];;
NSInteger pointsCount = pointsArray.count;
CLLocationCoordinate2D pointsToUse[pointsCount];
for(int i = 0; i < pointsCount; i++) {
CGPoint p = CGPointFromString(pointsArray[i]);
pointsToUse[i] = CLLocationCoordinate2DMake(p.x,p.y);
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:pointsToUse count:pointsCount];
[self.mapView addOverlay:myPolyline];
//NSLog(#"Drawed %#",pointsArray);
}
}];
}
I get the value of [objects valueForKey:#"Path"]
(
(
"{37.331622, -122.030337}",
"{37.331593, -122.03051}",
"{37.331554, -122.030681}",
"{37.331383, -122.030757}",
"{37.33108, -122.030772}",
"{37.330798, -122.030729}",
"{37.330636, -122.030636}"
) )
But i want it to
(
"{37.331622, -122.030337}",
"{37.331593, -122.03051}",
"{37.331554, -122.030681}",
"{37.331383, -122.030757}",
"{37.33108, -122.030772}",
"{37.330798, -122.030729}",
"{37.330636, -122.030636}"
)
What should i do?
It looks as if objects is an array of one element, which is an object with the "Path"
property. In that case you should replace
NSArray *pointsArray = [objects valueForKey:#"Path"];
by
NSArray *pointsArray = [objects[0] valueForKey:#"Path"];
In your example you have the objectId. If that is the case, you use getObjectWithId. This will return only the object you want.
PFQuery *query = [PFQuery queryWithClassName:#"Session"];
[query getObjectWithId:#"hr46gjh45"];
Your example returns an array of objects; in this case the array has only one object (but it is still an array). In that case you must first retrieve the object from the array.

How to get the objectId from NSArray from PFquery in PARSE.com

My PFQuery returns the following description.I am trying to fetch the objectId of the class
YouQuestions.For eg: in the below description Ovx3B1TnaC is the objectId for the first index of quesAray. But I have no idea on how to fetch it.
Printing description of quesArray:
<__NSArrayM 0xe1198f0>(
<YouQuestions:OVx3BlTnaC:(null)> {
askedUser = "<PFUser:XGvZsNyg9p>";
attachedImage = "<PFFile: 0xb4c9d20>";
category = Business;
geoLocation = "<PFGeoPoint: 0xb4c9ea0>";
question = "Who is kaka?";
thumbImage = "<PFFile: 0xb4c9dd0>";
},
This is how I did but returned nil
PFQuery *fetchTimeLine = [PFQuery queryWithClassName:#"YouQuestions"];
[fetchTimeLine whereKeyExists:#"objectId"];
[fetchTimeLine findObjectsInBackgroundWithBlock:^(NSArray *quesArray, NSError *error)
{
for (int i =0; i<quesArray.count; i++)
{
PFObject *obj = [quesArray[i] objectForKey:#"objectId"];
[searchobjectIDsArray addObject:obj.objectId];
}
}];
EDIT:
I fixed it like this
for (PFObject *object in quesArray) {
NSLog(#"%#", object.objectId);
}
to get the array of ids:
NSArray *oids = [quesArray valueForKeyPath:#"objectId"];

Resources