From the following array I want to fetch dictionary with message_id = 1. Basically I want to get the indexpath of the cell whose message_id == 1.
<__NSArrayM 0x17d918d0>(
<__NSArrayM 0x17faa070>(
{
chatStatus = sent;
date = "Feb 05, 2016";
from = "";
height = "17.000000";
isOutgoing = yes;
"message_id" = "1";
time = "02:39 PM";
to = "";
width = "95.000000";
},
{
chatStatus = sent;
date = "Feb 05, 2016";
from = "";
height = "17.000000";
isOutgoing = yes;
"message_id" = "2";
time = "02:39 PM";
to = "";
width = "95.000000";
},
)
)
I think you should look around the NSArray's method
-[NSArray indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)]
by doing something like that :
NSMutableArray *indexPaths = [NSMutableArray array];
[myArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger firstIndex, BOOL * _Nonnull stop) {
NSArray *messages = (NSArray*)obj;
NSIndexSet *indexes = [messages indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *message = (NSDictionary*)obj;
if (message.message_id == 1) {
return YES;
}
return NO;
}];
[indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[indexPaths addObject:[NSIndexPath indexPathForRow:idx inSection:firstIndex]];
}];
})];
It's seems a bit tricky, and I did it quickly so that I think you could improve it. The idea is to enumerate through your first array, and then use "indexesOfObjectsPassingTest" to get all indexes for your matching objects. And then you and your indexPaths by going through the enumeration of the NSIndexSet object :)
I think you could even do it recursively, so that you won't be limited by the depth of your arrays which is, here, only 2 :/
Hope it helped \o
Bilkix.
Related
I have an array of dictionaries that I am trying to get the Max score for each player in the array. Each player can have multiple entries I am trying to get an array of dictionaries of each players best score.
NSArray
[0] - NSDictionary
- [0] Score: (double)20.7
- [1] NameID: (int) 1
- [2] Date
[1] - NSDictionary
- [0] Score: (double)25
- [1] NameID: (int) 1
- [2] Date
[2] - NSDictionary
- [0] Score: (double)28
- [1] NameID: (int) 2
- [2] Date
[3] - NSDictionary`
- [0] Score: (double)26
- [1] NameID: (int) 3
- [2] Date
I have tried using NSPredicate predicateWithFormat but I am only able to get back the max for everything in the array not related to the name.
Expected Output:
NSArray
[1] - NSDictionary
- [0] Score: (double)25
- [1] NameID: (int) 1
- [2] Date
[2] - NSDictionary
- [0] Score: (double)28
- [1] NameID: (int) 2
- [2] Date
[3] - NSDictionary`
- [0] Score: (double)26
- [1] NameID: (int) 3
- [2] Date
Thanks for the help.
You can't use an NSPredicate for this, since you want to determine the maximum score for several different players. Under the covers, NSPredicate iterates the array anyway, so using your own loop isn't any less efficient. In the following code I have assumed that the scores and player names are wrapped in NSNumber
-(NSArray *)maxScoresForPlayers:(NSArray *)playerScores {
NSMutableDictionary *maxScores = [NSMutableDictionary new];
for (NSDictionary *player in playerScores) {
NSNumber *playerID = (NSNumber *)player[#"NameID"];
NSDictionary *playerMax = maxScores[playerID];
if (playerMax == nil) {
playerMax = player;
} else {
NSNumber *currentMax = (NSNumber *)[playerMax[#"Score"];
NSNumber *playerScore = (NSNumber *)player[#"Score"];
if ([playerScore doubleValue] > [currentMax doubleValue]) {
playerMax = player;
}
}
maxScores[playerID] = playerMax;
}
return([maxScores allValues];
}
You can do it manually like this:
NSMutableDictionary *maxScoresDict = [NSMutableDictionary dictionary];
for (NSDictionary *score in scoresArray) {
NSNumber *key = score[#"NameID"];
NSNumber *savedMax = maxScoresDict[key][#"Score"];
NSNumber *currentMax = maxScoresDict[key][#"Score"];
if (savedMax == nil || [currentMax doubleValue] > [savedMax doubleValue]) {
maxScoresDict[key] = score;
}
}
NSArray *maxScoresArray = [maxScoresDict allValues];
Try this:
NSArray *objects = #[#{#"Score": #(20.7),
#"NameID": #(1),
#"Date": [NSDate date]},
#{#"Score": #(25),
#"NameID": #(1),
#"Date": [NSDate date]},
#{#"Score": #(28),
#"NameID": #(2),
#"Date": [NSDate date]},
#{#"Score": #(26),
#"NameID": #(3),
#"Date": [NSDate date]}];
NSMutableArray *users = [NSMutableArray array];
for (NSInteger i=0; i<objects.count; i++) {
NSDictionary *dict = objects[i];
NSNumber *nameID = dict[#"NameID"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.NameID==%#", nameID];
NSInteger index = [users indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
BOOL found = [predicate evaluateWithObject:obj];
return found;
}];
if (index != NSNotFound) {
NSNumber *score1 = dict[#"Score"];
NSNumber *score2 = users[index][#"Score"];
if (score1.doubleValue > score2.doubleValue) {
[users replaceObjectAtIndex:index withObject:dict];
}
}
else {
[users addObject:dict];
}
}
NSLog(#"%#", users);
- (NSArray *)getBestScores:(NSArray *)players {
NSMutableDictionary *best = [[NSMutableDictionary alloc] init];
for (NSDictionary *p in players) {
NSDictionary *b = [best valueForKey:[p valueForKey:#"NameID"]];
if (!b || [[p valueForKey:#"Score"] doubleValue] > [[b valueForKey:#"Score"] doubleValue])
[best setValue:p forKey:[p valueForKey:#"NameID"]];
}
return [best allValues];
}
// Get Max Value of integer element from Array of Dictonaries.
// Example Array Starts
<paramArray>(
{
DicID = 1;
Name = "ABC";
ValuetoCalculateMax = 2800;
},
{
DicID = 2;
Name = "DEF";
ValuetoCalculateMax = 2801;
},
{
DicID = 3;
Name = "GHI";
ValuetoCalculateMax = 2805;
}
)
// Example Array Ends
// Implementation
int MaxintegerValue=0;
MaxintegerValue=[self getMaxValueFromArrayofDictonaries:paramArray];
// Implementation Ends
// Function Starts
-(int)getMaxValueFromArrayofDictonaries:(NSArray *)paramArray
{
int MaxValue=0;
NSMutableDictionary *dic=[[NSMutableDictionary alloc]init];
for ( int i=0; i<[paramArray count]; i++ )
{
dic=[paramArray objectAtIndex:i];
if ([[dic valueForKey:#"ValuetoCalculateMax"] intValue] > MaxValue)
{
MaxValue=[[dic valueForKey:#"ValuetoCalculateMax"] intValue];
}
else
{
MaxValue=MaxValue;
}
}
return MaxValue;
}
// Function Ends
What you need to do is find scores for each user, then find the max score out of it.
- (void)findMaxScoreForUser:(int)userId {
NSDictionary *dict0 = [NSDictionary dictionaryWithObjects:#[#27.0,#3] forKeys:#[#"Score",#"UserID"]];
NSDictionary *dict1 = [NSDictionary dictionaryWithObjects:#[#25.0,#2] forKeys:#[#"Score",#"UserID"]];
NSDictionary *dict2 = [NSDictionary dictionaryWithObjects:#[#23.0,#3] forKeys:#[#"Score",#"UserID"]];
NSArray *arr = [NSArray arrayWithObjects:dict0,dict1,dict2, nil];
NSMutableArray *scores = [NSMutableArray array];
for (NSDictionary *dict in arr) {
int userID = [[dict valueForKey:#"UserID"] intValue];
if (userId == userID) {
[scores addObject:[dict valueForKey:#"Score"]];
}
}
int max = [[scores valueForKeyPath:#"#max.intValue"] intValue];
}
I have a NSArray of NSDictionary objects:
[
{
id = 1;
fromDate = 2014-04-03;
toDate = 2014-04-05;
date = 0000-00-00;
title = title 1
},
{
id = 1;
fromDate = 0000-00-00;
toDate = 0000-00-00;
date = 2014-04-03
title = title 1
},
{
id = 1;
fromDate = 0000-00-00;
toDate = 0000-00-00;
date = 2014-04-04;
title = title 1
},
{
id = 2;
fromDate = 0000-00-00;
toDate = 0000-00-00;
date = 2014-05-10;
title = title 2
},
{
id = 2;
fromDate = 0000-00-00;
toDate = 0000-00-00;
date = 2014-05-11;
title = title 2
}
]
I would like to merge dictionaries with same id value into one dictionary combining all date, fromDate and toDate keys, obtaining an array like this, that ignores zero values:
[
{
id = 1,
combinedDates = 2014-04-03, 2014-04-05, 2014-04-03, 2014-04-04;
title = title 1
},
{
id = 2,
combinedDates = 2014-05-10, 2014-05-11;
title = title 2
}
]
Can someone point me to the right direction?
I don't know of any way to do this other than basic brute force:
-(NSArray*)combinedArray:(NSArray*)array
{
NSMutableArray* combined = [NSMutableArray new];
// Iterate over each unique id value
for(id key in [NSSet setWithArray:[array valueForKeyPath:#"id"]])
{
// skip missing keys
if([key isKindOfClass:[NSNull class]])
continue;
// Sub array with only id = key
NSArray* filtered = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary* evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject valueForKey:#"date"] && [[evaluatedObject valueForKey:#"id"] isEqual:key];
}]];
// Grab the dates
NSArray* dates = [filtered valueForKeyPath:#"date"];
// add the new dictionary
[combined addObject:#{ #"id":key, #"combinedDates":dates }];
}
return array;
}
// Group By id
-(NSMutableDictionary*)combinedArray:(NSArray*)array
{
NSMutableArray *categories = [[NSMutableArray alloc] init];
for (NSDictionary *data in array) {
NSString *category = [data valueForKey:#"id"];
if (![categories containsObject:category]) {
[categories addObject:category];
}
}
NSMutableDictionary *categoryData = [[NSMutableDictionary alloc]init];
for (NSString *strCat in categories) {
NSMutableArray *catArray = [[NSMutableArray alloc]init];
for (NSDictionary *data in array) {
NSString *category = [data valueForKey:#"id"];
if ([category isEqualToString:strCat]) {
[catArray addObject:data];
}
}
[categoryData setObject:catArray forKey:strCat];
}
return categoryData;
}
My application crashed at following codes:
NSMutableArray* array = [dict objectForKey:key];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];
And the crash information is:
1 CoreFoundation 0x2ecd30f2 __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_invoke (in CoreFoundation) + 90
2 CoreFoundation 0x2ecd3024 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] (in CoreFoundation) + 232
I can't figure it out, anyone knows how to solve this problem?
try to use:
[word isEqualToString:#"jerry"]
Try this:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
stop = YES;
find = YES;
}
You are comparing strings in the wrong way.
Try this
NSMutableArray* array = [NSMutableArray arrayWithArray:[dict objectForKey:key]];
if (array)
{
__block BOOL find = NO;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString* word = (NSString*)obj;
if ([word isEqualToString:#"jerry"]) {
*stop = YES;
find = YES;
}
}];
My money is on your array turning out to not actually be an array. When you create and set your array from the object in dict, check to see that [dict objectForKey:key] is actually an array.
NSMutableArray* array = [dict objectForKey:key];
NSLog(#"class of array: %#", [[dict objectForKey:key] class]);
I have the next NSMutableArray:
Product[0] {
name => val1,
Price => pricevalue 1
}
Product[1] {
name => val2,
Price => pricevalue2
}
I want to search for: name = val2 and return the index of product, so 1.
Use like this
NSIndexSet *indices = [array
indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[obj objectForKey:#"name"] isEqualToString:#"val2"];
}];
Happy coding.........
__block NSUInteger index = NSUIntegerMax;
[products enumerateObjectsUsingBlock: ^ (Product* product, NSUInteger idx, BOOL* stop) {
if([product.name isEqualToString:#"val2"])
{
index = idx;
*stop = YES;
}
}];
Edit: Ravindra's solution is more elegant!
How can I enumerate an array starting at the center of the array?
#implementation NSArray (Extensions)
- (void)enumerateFromCenterGoBothWaysUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
{
NSMutableArray *copy = [self mutableCopy];
BOOL shouldStop = NO;
while([copy count] > 0 && shouldStop == NO)
{
NSUInteger index = [copy count] / 2;
id obj = copy[index];
[copy removeObject:obj];
block(obj, index, &shouldStop);
}
}
#end