I have an array that has the past 5 days. It is built like this:
(
"2015-01-27",
"2015-01-26",
"2015-01-25",
"2015-01-24",
"2015-01-23",
)
I have a second NSArray from a FetchRequest
(
{
daySectionIdentifier = "2015-01-24";
sumValue = 2500;
},
{
daySectionIdentifier = "2015-01-25";
sumValue = 1487;
},
{
daySectionIdentifier = "2015-01-27";
sumValue = 750;
}
)
What I want is the dates that match my first array get a value in the first array, the missing dates get no value.
So the final result will look like this:
(
{
daySectionIdentifier = "2015-01-23";
sumValue = 0;
},
{
daySectionIdentifier = "2015-01-24";
sumValue = 2500;
},
{
daySectionIdentifier = "2015-01-25";
sumValue = 1000;
},
{
daySectionIdentifier = "2015-01-26";
sumValue = 0;
},
{
daySectionIdentifier = "2015-01-27";
sumValue = 750;
}
)
Anybody have an idea how to do this? Thanks in advance
Ok so this didn't turn out to be too hard, hopefully this is what you are after:
Firstly thinking about the problem, the issue somewhat here is getting the data in the right format to be able to analyse, so first of all I changed it from an array filled with dictionaries to an array of arrays with each array containing the information (I know not the most elegant solution but one that works none the less)
// Here is our array of past dates
NSArray * pastDateDays = #[#"2015-01-27", #"2015-01-26", #"2015-01-25", #"2015-01-24", #"2015-01-23"];
// Here is our array from the request, this is full of dictionaries
NSArray * fetchRequest = #[#{#"daySectionIdentifier" : #"2015-01-24", #"sumValue": #2500}, #{#"daySectionIdentifier" : #"2015-01-25", #"sumValue": #1487}, #{#"daySectionIdentifier" : #"2015-01-27", #"sumValue": #750}];
// Here is a mutable array we will be adding to
NSMutableArray * request = [NSMutableArray arrayWithArray:fetchRequest];
So now we are ready to start getting the information into a slightly nicer format.
// This function gets the array in an array of arrays where each array has a date and a value
fetchRequest = [self fetchRequestToArray:fetchRequest];
// Not too complicated just taking it out of one and putting it in another
- (NSArray *)fetchRequestToArray: (NSArray *)array {
NSMutableArray * tempArray = [NSMutableArray new];
for (NSDictionary * dict in array) {
NSArray * temp = #[[dict objectForKey:#"daySectionIdentifier"], [dict objectForKey:#"sumValue"]];
[tempArray addObject:temp];
}
return [NSArray arrayWithArray:tempArray];
}
Next we loop through a mutable array of the dates in our date array and if they match in our requested array we remove them:
NSMutableArray * tempDates = [NSMutableArray arrayWithArray:pastDateDays];
for (NSArray * array in fetchRequest) {
NSString * date = array.firstObject;
for (NSString * string in pastDateDays) {
if ([date isEqualToString:string]) {
[tempDates removeObject:string];
}
}
}
This leaves us with an array of dates which are included in our date array but are not included in our requested data. These are the dates we need to add a zero value for.
Again this is relatively simple:
for (NSString * date in tempDates) {
NSDictionary * dict = [NSDictionary dictionaryWithObjects:#[date, #0]
forKeys:#[#"daySectionIdentifier", #"sumValue"]];
[request addObject:dict];
}
This returns us with the desired array.
The only thing that might need to be added is that this array isn't in date order. This can be easily sorted with a number of methods. I found and added this on in a few seconds but you could choose a more complicated one if you need it:
NSSortDescriptor * sortByDate = [NSSortDescriptor sortDescriptorWithKey:#"daySectionIdentifier"
ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObject:sortByDate];
NSArray * sortedArray = [request sortedArrayUsingDescriptors:sortDescriptors];
This will output the date in the format:
The final array is the array called request and is a mutableArray
<__NSArrayI 0x7fade848f480>(
{
daySectionIdentifier = "2015-01-23";
sumValue = 0;
},
{
daySectionIdentifier = "2015-01-24";
sumValue = 2500;
},
{
daySectionIdentifier = "2015-01-25";
sumValue = 1487;
},
{
daySectionIdentifier = "2015-01-26";
sumValue = 0;
},
{
daySectionIdentifier = "2015-01-27";
sumValue = 750;
}
)
Which I think is the desired output.
Things to note:
- The values are NSNumbers and not integers as we can't store integers in an NSdictionary
This is not the most elegant solution - I have used a lot of arrays and i am sure it could be refactored. This code though does work and so can be worked with to build understanding - this code should work when copied straight in but there may be a few things needing tweaking as it is a long answer copied from my XCode
The strings need to be in exactly this format for it to work, if they are not then this solution will need to be tweaked.
I hope this helps
Related
I have an array of dictionaries that contain information about high scores. I am trying to figure out how to sort them by the different values in the dictionaries but cannot get it to work.
An example shown below attempts to sort by "Score":
NSDictionary *highScoreDictionary1 = #{#"Score" : #52, #"Duration" : #230 , #"Date" : [NSDate date]};
NSDictionary *highScoreDictionary2 = #{#"Score" : #23, #"Duration" : #230 , #"Date" : [NSDate date]};
NSDictionary *highScoreDictionary3 = #{#"Score" : #35, #"Duration" : #230 , #"Date" : [NSDate date]};
NSArray *highScoresArray = #[highScoreDictionary1, highScoreDictionary2, highScoreDictionary3];
NSSortDescriptor *highScoreSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"Score" ascending:YES]; // Sort by Score
NSArray *sortDescriptorArray = [NSArray arrayWithObject:highScoreSortDescriptor];
[highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];
The output I get from NSLog(#"sorted array of dictionaries: %#", highScoresArray); is:
sorted array of dictionaries: (
{
Date = "2014-09-01 19:38:00 +0000";
Duration = 230;
Score = 52;
},
{
Date = "2014-09-01 19:38:00 +0000";
Duration = 230;
Score = 23;
},
{
Date = "2014-09-01 19:38:00 +0000";
Duration = 230;
Score = 35;
}
)
How do I remedy this? Am I missing something here because it seems that the dictionaries are not being sorted by score.
highScoresArray = [highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];
You're trying to sort an NSArray, which is immutable. You need to use the sort function to create a mutable array, i.e.
replace your:
[highScoresArray sortedArrayUsingDescriptors:sortDescriptorArray];
with:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:[highScoresArray sortedArrayUsingDescriptors:#[highScoreSortDescriptor]]];
I have tested this and it seems to work.
Try this approach
NSArray *sortedArray;
sortedArray = [highScoresArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDictionary *first = (NSDictionary*)a;
NSDictionary *second = (NSDictionary*)b;
int firstScore = [first objectForKey:#"score"];
int secondScore = [second objectForKey:#"score"];
if(firstScore > secondScore)
{
return NSOrderedDescending;
}
else if (firstScore < secondScore)
{
return NSOrderedAscending;
}
return NSOrderedSame;
}];
Got the code from here
If you would like, here's my own sorting method which I implemented manually, in your case just use it like this
// you could also pass "DESC" for descending order
NSMutableArray* copiedArray = [[NSMutableArray alloc] initWithArray:highScoresArray];
[self sortArray:copiedArray inOrder:#"ASC" basedOnField:#"Score" args:-1];
// Now copiedArray contains a sorted array :)
Here's the full code (2 methods, one main and one helper), copy these to some class so the above code would work.
/*
* This method sorts a given array based on the given field name and the given sorting order
* fieldName could be nil if the comparison shall happen directly on the array items
* args contain the array index of the value to compare if "field name" pointed to an array or -1
*/
- (void)sortArray:(NSMutableArray*)array
inOrder:(NSString*)sortingOrder
basedOnField:(NSString*)fieldName
args:(int)args {
for (int i = 1; i < array.count; i++) {
// Start the insertion sort algorithm
int j = i;
// Get the current value and one before it
id currentValue = [self itemInArray:array
atIndex:j
fieldName:fieldName
args:args];
id previousValue = [self itemInArray:array
atIndex:j-1
fieldName:fieldName
args:args];
// Set the comparison result based on the user request
NSComparisonResult requiredResult = NSOrderedDescending;
if ([sortingOrder compare:#"ASC"] == NSOrderedSame) {
requiredResult = NSOrderedAscending;
}
while ((j > 0) && ([previousValue compare:currentValue] == requiredResult)) {
// Swap the current and previous objects
id temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
// Get back one step and get the new current and previous values
j--;
if (j == 0) {
break;
}
currentValue = [self itemInArray:array
atIndex:j
fieldName:fieldName
args:args];
previousValue = [self itemInArray:array
atIndex:j-1
fieldName:fieldName
args:args];
}
}
}
// This method gets an item from the array based on the given index and the field name if the item is an object, as well as a specific member of that item if it's an array (index is passed in args)
- (id)itemInArray:(NSArray*)array
atIndex:(int)index
fieldName:(NSString*)fieldName
args:(int)args {
// Get the item at the given index
id value = array[index];
// Get the sepcific field from it if it's an object
if (fieldName != nil) {
value = [value valueForKey:fieldName];
}
// Get the specific value if the field is an array
if ([value isKindOfClass:[NSArray class]]) {
value = value[args];
}
return value;
}
I am trying to manipulate an array of dictionaries based on an array of values.
for example:
arrayOfDicts =
(
{
caption = a;
urlRep = "12";
},
{
caption = b;
urlRep = "34";
},
{
caption = c;
urlRep = "56";
}
)
Array of values:
urlReps = (12,56);
outcome I am trying to achieve:
(
{
caption = a;
urlRep = "12";
},
{
caption = c;
urlRep = "56";
}
)
The code I have now that adds to it based on the array is this:
NSMutableArray *arrayOfDicts;
NSMutableSet *urlReps;
[urlReps minusSet:[NSSet setWithArray:[arrayOfDicts valueForKey:#"urlRep"]]];
// merge new dicts to the original array
for (id urlRep in urlReps)
{
[arrayOfDicts addObject:#{ #"urlRep" : urlRep, #"caption" : #"" }];
}
This adds to my array of dicts if there are more urls in the array but I need to also remove if there are less urls in the array compared to the dict
Try something Like this using NSPredicate to filter the array:
NSArray *arrayOfDicts = .... //your existing data
NSArray *filteredURLParams = #[#"12",#"56"];
NSPredicate *urlPredicate = [NSPredicate predicateWithFormat:#"urlRep IN %#",filteredURLParams];
NSArray *filteredDicts = [arrayOfDicts filteredArrayUsingPredicate:urlPredicate];
Here's some old fashioned, straightforward, and completely untested code :-)
// Your data
NSMutableArray* arrayOfDicts = [...];
NSMutableSet* urlReps = [...];
// Will receive those dictionaries that have a matching urlRep
NSMutableArray* filteredArrayOfDicts = [NSMutableArray arrayWithCapacity:0];
// Initially contains all urlReps, but we will successively
// eliminate those urlReps that we encountered
NSMutableSet* urlRepsNotSeen = [NSMutableSet setWithCapacity:0];
[urlRepsNotSeen addObjects:[urlReps allObjects]];
for (NSDictionary* dict in arrayOfDicts)
{
NSString* urlRep = [dict valueForKey:#"urlRep"];
if ([urlReps containsObject:urlRep])
[
[filteredArrayOfDicts addObject:dict];
// Not sure what happens if urlRepsNotSeen does not contain the
// urlRep (because we eliminated it earlier). If it crashes, add
// this check:
// if ([urlRepsNotSeen containsObject:urlRep])
[urlRepsNotSeen removeObject:urlRep];
}
arrayOfDicts = filteredArrayOfDicts;
for (NSString urlRepNotSeen in urlRepsNotSeen)
{
[arrayOfDicts addObject:#{ #"urlRep" : urlRepNotSeen, #"caption" : #"" }];
}
I am trying to take an array and merge it into an array of dictionaries but unsure as to how to do it.
I have an array of dictionaries that looks like this:
(
{
caption = a;
urlRep = "12";
},
{
caption = b;
urlRep = "34";
},
{
caption = c;
urlRep = "56";
}
)
and given an array like this:
(12,34,56,78)
I want to merge it into my dictionaries to make it look like this:
(
{
caption = a;
urlRep = "12";
},
{
caption = b;
urlRep = "34";
},
{
caption = c;
urlRep = "56";
},
{
caption = "";
urlRep = "78";
}
)
edit:
I need to also consider removing from the array of dicts if the given array does not contain one of the urlReps.
Any help would be greatly appreciated as I've been stuck trying to figure this out for some time.
Here's a simple, efficient and elegant solution using NSSets to handle unique keys:
NSMutableArray *arrayOfDicts; // your input array of dictionaries
NSArray *urlRepArray; // the new array with string elements
// create a set of potentially new keys (urlReps)
NSMutableSet *urlReps = [NSMutableSet setWithArray:urlRepArray];
// remove existing keys from your original array
[urlReps minusSet:[NSSet setWithArray:[arrayOfDicts valueForKey:#"urlRep"]]];
// merge new dicts to the original array
for (id urlRep in urlReps)
[arrayOfDicts addObject:#{ #"urlRep" : urlRep, #"caption" : #"" }];
Easiest way AFAIK, Filter using valueForKeyPath
//Your array of dictionary I created here for debugging purpose.
NSArray *tmpArray = #[ #{#"caption":#"a",#"urlRep":#"12"},
#{#"caption":#"b",#"urlRep":#"34"},
#{#"caption":#"c",#"urlRep":#"56"}];
//This will give you 12,34,56 in your case
NSArray *existingURLRep = [tmpArray valueForKeyPath:#"urlRep"];
NSMutableArray *targetArray = [[NSMutableArray alloc] initWithObjects:#12, #34,#56, #78, nil]; //Assuming you have your array as you said
[targetArray removeObjectsInArray:existingURLRep];
//remove existing items you will have 78 here now loop through
//this targetArray and add it to your array of dictionary.
(void)filterArray{
NSLog(#"Array before filtering = %#",initialArray);
NSLog(#"given Array = %#",givenArray);
NSMutableSet *urlReps = [NSMutableSet setWithArray:givenArray];
// remove existing records
[urlReps minusSet:[NSSet setWithArray:[initialArray valueForKey:#"urlRep"]]];
// adding new objects
for (id obj in urlReps) {
[initialArray addObject:#{#"caption":#"", #"urlRep" : obj}];
}
// removing objects
NSMutableSet *set = [[NSMutableSet alloc] init];
for (id obj in initialArray) {
NSDictionary *dict = (NSDictionary *)obj;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self = %#", dict[#"urlRep"]];
NSArray *filteredArray = [givenArray filteredArrayUsingPredicate:predicate];
if(filteredArray.count == 0) {
[set addObject:dict];
}
}
[initialArray removeObjectsInArray:[set allObjects]];
NSLog(#"Array after filtering = %#",initialArray);
}
NSMutableArray *yourArray;//This will be your original array of dictionary.
NSArray *newArray;//This is your new array which you want to add.
for(id obj in newArray) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"urlRep = %#", id];
NSArray *filteredArray = [locationsArray filteredArrayUsingPredicate:predicate];
if(filteredArray.count == 0) {
[yourArray addObject:#{#"caption":#"", #"urlRep" : id}];
}
}
/*
NSArray *inputArray;//(12,34,56,78)- I assumes you are having array which contains strings. If you are having number then modify the code as you needed
NSMutableArray *colloectionArray;// your total collection
NSMutableArray *tobeMerged;
*/
// Extract the dictionary set only to be merged
for (NSString* aNumber in inputArray) {
for (NSDictionary *aItem in colloectionArray) {
NSString *urlRep= [aItem valueForKey:#"urlRep"];
if (![urlRep isEqualToString:aNumber]) {
[tobeMerged addObject:urlRep];
}
}
}
// Add missed items in collection
for (NSString *aNumber in tobeMerged) {
NSMutableDictionary *newset = [[NSMutableDictionary alloc]init];
[newset setObject:#"" forKey:#"caption"];
[newset setObject:aNumber forKey:#"urlRep"];
[colloectionArray addObject:newset];
}
I'm horrible at JSON. I don't understand a single thing. My JSON response looks like this:
{
ID = 1;
EDate = "<null>";
SelectedDay = "/Date(-62135596800000)/";
End = "14.09.2013 15:00:00";
Start = "14.09.2013 07:00:00";
SDate = "<null>";
},
{
ID = 1;
EDate = "<null>";
SelectedDay = "/Date(-62135596800000)/";
End = "14.09.2013 16:00:00";
Start = "14.09.2013 07:00:00";
SDate = "<null>";
},
In both NSData and NSDictionary. How can I loop trough, for example, the "End" property of each object, and add them to an array?
Edit:
I log from this code:
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result.data options:kNilOptions error:&error];
NSLog(#"Response: %#",dict);
and the complete log is:
This JSON seems to be an array of dictionaries. Try with:
NSMutableArray *endValuesArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in JSONArray) {
[endValuesArray addObject:[dictionary valueForKey:#"End"]];
}
Where JSONArray is the array obtained after NSJSONSerialization.
If you really just need an array of the values for a single key in each dictionary then you can use KVC:
NSArray *endValues = [resultsArray valueForKey:#"End"];
-- This is assuming that you do have an array of dictionaries and that your pasted log just doesn't show the full story.
If you need multiple keys / values out of the dictionaries then you're best to iterate over the contents and pick each item. There are various methods of iteration that you can look at using plain loops or blocks.
Use this :
NSMutableArray *endDatesArray = [NSMutableArray new]; // Here this array will store all end dates
for (int i =0; i < [YOUR_JSONARRAY count]; i++) // Here YOUR_JSONARRAY is the response array you are getting
{
NSMutableDictionary *dict= [YOUR_JSONARRAY objectAtIndex:i];
[endDatesArray addObject:[dict objectForKey:#"End"]];
}
Hope it helps you.
Make an NSArray of the JSON Object.
Use a FOR loop up to the count of the array to create an NSDictionary for each array object
Use 'objectForKey:#"End"' to extract the End object. (within the for loop)
I have an NSMutableArray that has NSDictionaries in it. I found a question on SO, and am using this code to sort this NSMutableArray:
NSMutableArray
{
data = {
etc... (for length)
};
number = 1;
},
{
data = {
etc... (for length)
};
number = 3;
},
{
data = {
etc... (for length)
};
number = 4;
},
{
data = {
etc... (for length)
};
number = 2;
}
This array goes up to 80 sub Dictionaries.
NSSortDescriptor
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"number" ascending:YES];
[NSMUTABLEARRAYVAR sortedArrayUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]];
NSMutableArray *test;
test = [NSMUTABLEARRAYVAR copy];
When I NSLog "test" I get the same order as NSMUTABLEARRAYVAR???
I would appreciate any help in solving this issue, thanks!
sortedArrayUsingDescriptors does not sort the array in place. It returns a newly-sorted array.
sortUsingDescriptors does an in-place sort.