Get key values from array of dictionaries - objective-c

I have an array of values for example a = [1,2,3] and array of dictionaries b =[{2:"a"},{3:"b"},{45:"r"},{16:"a"}]. How can I get values from b which keys match the values in the array. Can someone give me a hint? Thanks.

In objective-c
NSMutableArray *c = [[NSMutableArray alloc] init];
for (NSNumber *i in a) {
BOOL iFound = NO;
for (NSDictionary *dict in b) {
if ((NSNumber *)[[dict allKeys] firstObject] == i) {
iFound = YES;
[c addObject:(NSString *)[[dict allValues] firstObject]];
}
}
if (!iFound) {
[c addObject: [NSNull null]];
}
}
NSLog(#"%#",c);//("<null>",a,b)
In swift
let a = [1,2,3]
let b = [[2:"a"],[3:"b"],[45:"r"],[16:"a"]]
let c = a.map { i in b.first(where: { $0.keys.first == i })?.values.first }
print(c)//[nil, Optional("a"), Optional("b")]

Related

ObjectForKey is returning nil sometimes

I am trying to set an object to a dictionary with key as an object. The test cases works fine, but within the actual code, I am not able to get the value for the key. The NSMutableDictionary has the key value in it, but when debugging it returns nil.
#implementation JSHashMap {
NSMutableDictionary *dict;
}
- (instancetype)initWithArray:(NSMutableArray *)array {
self = [super init];
if (self) {
dict = [self fromArray:array];
}
return self;
}
- (NSMutableDictionary *)fromArray:(NSMutableArray *)array {
NSMutableDictionary* _dict = [NSMutableDictionary new];
NSUInteger i = 0, len = [array count];
if (len % 2 != 0) {
error(#"JSError: Odd number of elements in the array.");
return _dict;
}
for (i = 0; i < len; i = i + 2) {
[_dict setObject:array[i + 1] forKey:array[i]];
assert([_dict objectForKey:array[i]] != nil);
}
debug(#"%#", _dict);
return _dict;
}
- (JSData *)objectForKey:(id)key {
return [dict objectForKey:key];
}
I am creating the hash map using the initWithArray method.
(lldb) po [dict objectForKey:key]
nil
The key passed in and the key in the dictionary has the same memory address 0x100ea2fa0.
The test cases works fine though. But the when running the actual program, it fails.
NSMutableDictionary *dict = [NSMutableDictionary new];
JSNumber *val = [[JSNumber alloc] initWithInt:1];
JSNumber *key = [[JSNumber alloc] initWithInt:2];
[dict setObject:val forKey:key];
JSData * ret = [dict objectForKey:key];
XCTAssertNotNil(ret);
XCTAssertEqualObjects([ret dataType], #"JSNumber");
JSHashMap *hm = [[JSHashMap alloc] initWithArray:[#[key, val] mutableCopy]];
JSData * ret1 = [hm objectForKey:key];
XCTAssertNotNil(ret1);
XCTAssertEqualObjects([ret1 dataType], #"JSNumber");
JSHashMap *dict = (JSHashMap *)ast;
NSArray *keys = [dict allKeys];
NSUInteger i = 0;
NSUInteger len = [keys count];
for (i = 0; i < len; i++) {
id key = keys[i];
JSData *val = (JSData *)[dict objectForKey:key];
// Issue -> val is getting nil
}
How to fix this and why is this random behaviour?
Found the failing test case.
NSArray *keys = [hm allKeys];
XCTAssertTrue([keys count] == 1);
JSData *ret = [hm objectForKey:keys[0]];
XCTAssertNotNil(ret);
If I use the key returned from calling allKeys method, it returns nil.
You have not shown any information about what JSNumber is, but I am betting that it does not implement isEqual and hash correctly. Thus, you cannot successfully use it as a key in an NSDictionary.

How to mapping array in YAPdatabase object?

i have test array with objects structure - Group with (NSMutableArray)items, and i save group in YapDatabase
-(void)parseAndSaveJson:(id) json withCompleteBlock:(void(^)())completeBlock{
NSMutableArray *groupsArray = (NSMutableArray *)json;
if (groupsArray != nil) {
YapDatabaseConnection *connectnion = [[DatabaseManager sharedYapDatabase] newConnection];
[connectnion asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (int groupIndex = 0; groupIndex < [groupsArray count]; groupIndex ++) {
LocalGroupsExercise *localGroup = [[LocalGroupsExercise alloc] init];
localGroup.name = groupsArray[groupIndex][LOCAL_GROUPS_NAME];
localGroup.tagColor = groupsArray[groupIndex][LOCAL_GROUPS_TAG_COLOR];
localGroup.idGroup = [groupsArray[groupIndex][LOCAL_GROUPS_ID_GROUP] intValue];
if (groupsArray[groupIndex][LOCAL_GROUPS_EXERCISES] != nil) {
NSMutableArray *exerciseArray = (NSMutableArray *)groupsArray[groupIndex][LOCAL_GROUPS_EXERCISES];
for (int exerciseIndex = 0; exerciseIndex < [exerciseArray count]; exerciseIndex ++) {
LocalExercise *localExercise = [[LocalExercise alloc] init];
localExercise.name = exerciseArray[exerciseIndex][EXERCISE_NAME];
localExercise.exerciseId = [exerciseArray[exerciseIndex][LOCAL_EXERCISE_ID_EXERCISE] intValue];
localExercise.groupId = localGroup.idGroup;
localExercise.type = [exerciseArray[exerciseIndex][EXERCISE_TYPE] intValue];
localExercise.minWeight = [exerciseArray[exerciseIndex][EXERCISE_MIN_WEIGHT] floatValue];
localExercise.maxWeight = [exerciseArray[exerciseIndex][EXERCISE_MAX_WEIGHT] floatValue];
localExercise.minReps = [exerciseArray[exerciseIndex][EXERCISE_MIN_REPS] intValue];
localExercise.maxReps = [exerciseArray[exerciseIndex][EXERCISE_MAX_REPS] intValue];
localExercise.minTimer = [exerciseArray[exerciseIndex][EXERCISE_MIN_TIMER] intValue];
localExercise.maxTimer = [exerciseArray[exerciseIndex][EXERCISE_MAX_TIMER] intValue];
localExercise.timeRelax = [exerciseArray[exerciseIndex][EXERCISE_RELAX_TIME] intValue];
[localGroup.exercises addObject:localExercise];
}
}
[transaction setObject:localGroup forKey:[NSString stringWithFormat:#"%d", localGroup.idGroup] inCollection:LOCAL_GROUPS_CLASS_NAME];
}
YapDatabaseConnection *connectnion = [[DatabaseManager sharedYapDatabase] newConnection];
[connectnion readWithBlock:^(YapDatabaseReadTransaction *transaction) {
LocalGroupsExercise *group = [transaction objectForKey:#"2" inCollection:LOCAL_GROUPS_CLASS_NAME];
NSLog(#"%#", group.name);
NSLog(#"%#", group.tagColor);
NSLog(#"%#", group.exercises);
}];
} completionBlock:^{
completeBlock();
}];
}
}
+ (YapDatabaseView *)setupDatabaseViewForShowGroupsGyms{
YapDatabaseViewGrouping *grouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
if ([object isKindOfClass:[LocalGroupsExercise class]]) {
LocalGroupsExercise *groupExercise = (LocalGroupsExercise *)object;
return [NSString stringWithFormat:#"%#", groupExercise.name];
}
return nil;
}];
YapDatabaseViewSorting *sorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, NSString *group, NSString *collection1, NSString *key1, LocalGroupsExercise *obj1, NSString *collection2, NSString *key2, LocalGroupsExercise *obj2) {
return [obj1.name compare:obj2.name options:NSNumericSearch range:NSMakeRange(0, obj1.name.length)];
}];
YapDatabaseView *databaseView = [[YapDatabaseView alloc] initWithGrouping:grouping sorting:sorting versionTag:#"0"];
return databaseView;
}
[[DatabaseManager sharedYapDatabase] registerExtension:self.databaseGroupView withName:LOCAL_GROUPS_CLASS_NAME];
[_connection beginLongLivedReadTransaction];
self.mappingsGroup = [[YapDatabaseViewMappings alloc] initWithGroupFilterBlock:^BOOL(NSString *group, YapDatabaseReadTransaction *transaction) {
return true;
} sortBlock:^NSComparisonResult(NSString *group1, NSString *group2, YapDatabaseReadTransaction *transaction) {
return [group1 compare:group2];
} view:LOCAL_GROUPS_CLASS_NAME];
[_connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[self.mappingsGroup updateWithTransaction:transaction];
}];
The problem is that the group be NSMutabblArray and I want to see the objects in the table of the array, but [self.mappingsGroup numberOfItemsInSection:section] return only one items in group
You need to configure YapDatabase to use Mantle. By default, it will use NSCoding. (Which is why you're seeing an error about "encodeWithCoder:", as that method is part of NSCoding.)
Take a look at YapDatabase's wiki article entitled "Storing Objects", which talks about how it uses the serializer/deserializer blocks: https://github.com/yaptv/YapDatabase/wiki/Storing-Objects
Basically, when you alloc/init your YapDatabase instance, you'll want to pass a serializer & deserializer block that uses Mantle to perform the serialization/deserialization.
Also, see the various init methods that are available for YapDatabase: https://github.com/yaptv/YapDatabase/blob/master/YapDatabase/YapDatabase.h

Part of NSString to NSNumber

I need to input x and y co-ordinates into a custom object, the input is with the format "x,y"
I am currently storing the input as an NSString and need to get the integers out of it and into separate NSNumbers. If there is another way to store the input that would be easier, please explain.
I need to store x and y as separate NSNumbers, this also this needs to account for if x and y are 2 digits. i.e. "23,4"
can anyone help?
Use -[NSString componentsSeparatedByString:]
NSArray *numericComponents = [string componentsSeparatedByString:#","];
NSArray *numbers = [numericComponents map:^id(NSString *object) {
return #([object integerValue]);
}];
map here is simply a category method that I've added to NSArray:
#implementation NSArray (JRAdditions)
- (NSArray *)map:(id(^)(id))block {
if([self count] == 0 || block == nil) return self;
NSMutableArray *mapped = [NSMutableArray new];
NSArray *copy = [self copy];
for(id obj in copy) {
id mappedObject = block(obj);
if(mappedObject) {
[mapped addObject:mappedObject];
}
}
return [mapped copy];
}
#end
NSString *str=#"23,4";
NSArray *array=[str componentsSeparatedByString:#","];
NSNumber *xNum=#([array[0] integerValue]);
NSNumber *yNum=#([array[1] integerValue]);
To check if they are two digits :
if ([xNum integerValue]>9 && [xNum integerValue]<100) {
NSLog(#"x is 2 digits");
}
else{
}
if([yNum integerValue]>9 && [yNum integerValue]<100) {
NSLog(#"y is 2 digits");
}
else{
}

NSUSerDefaults with NSMutableArray sort by desc order

I'm breaking my head on why descending order sort is not working with the following code. I wanted to limit by top 5 scores and other logic. The scores would look like this: 22/30, 12/18, 34/38, 23/32 etc. I added/removed SortDescriptor to sort by descending order and it seems to work for the first 3 items but then is not sorting properly. Can somebody help?
- (NSMutableArray*) method1:(NSString *) mode byScore: (NSString *) score
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[defaults objectForKey:mode]mutableCopy];
if (!array)
{
array = [[NSMutableArray alloc] init];
}
NSLog(#"The content of array is%#", array);
if ([array count] < 5)
{
if (![array containsObject:score])
{
[array addObject:score];
// Need to sort here. But not too sure this is the right place
NSLog(#"The content of the sorted array upto 5 is%#", array);
}
}
else
{
if (![array containsObject:score])
{
if ([array lastObject] < score)
{
[array addObject:score];
// Need to sort here before I remove the last object
[array removeLastObject];
NSLog(#"The content of the sorted array else is%#",array);
}
}
}
[defaults setObject:array forKey:mode];
[defaults synchronize];
// I want the array in NSUserDefaults to be sorted in desc order
// don't know what to return here ==> the array object or the defaults object cast to NSMutableArray?
}
Helper function
static NSComparisonResult CompareFloats( float a, float b )
{
if ( a < b ) { return NSOrderedAscending ; }
else if ( a > b ) { return NSOrderedDescending ; }
return NSOrderedSame ;
}
Category on NSString
#implementation NSString (Stuff)
-(float)floatValueForFraction
{
NSArray * components = [ self componentsSeparatedByString:#"/" ] ;
return [ components[0] floatValue ] / [ components[1] floatValue ] ;
}
#end
Your method:
- (void)addScore:(NSString*)score forMode:(NSString*)mode
{
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
NSArray * scores = [ defaults objectForKey:mode ] ;
scores = scores ? [ scores arrayByAddingObject:score ] : #[ score ] ;
scores = [ scores sortedArrayUsingComparator:^(NSString * a, NSString * b){
return CompareFloats( [ a floatValueForFraction ], [ b floatValueForFraction ] ) ;
}]
if ( scores.count > 5 ) { scores = [ scores subarrayWithRange:(NSRange){ .length = 5 } ] ; }
[ default setObject:scores forKey:mode ] ;
}
If you want the updated high scores after calling this method, just use [ [ NSUserDefaults standardUserDefaults ] objectForKey:<mode> ]. It's better to have your methods just do one thing.
One approach to sorting array:
First define a block getNumeratorAndDenominatorFromScoreString as follows:
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
Then use this block together with -[NSArray sortedArrayUsingComparator] to sort array:
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
This will order array from least to greatest. To order from greatest to least, just replace
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
with
if (value1 > value2) {
res = NSOrderedAscending;
} else if (value1 < value2) {
res = NSOrderedDescending;
}
A readable structure for this method would be, in [mostly not] pseudocode
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = //as above
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
//as above
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}
If you need to screen out scores the strings for which are not equal but the evaluations of the fractions for which are equal (#"1/2" and #"5/10"), you'll need to be more clever.
Here is the full code sketched out above:
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}

sortedArrayUsingComparator int

I have an array of a dictionary that it sortedArrayUsingComparator sorts them based on the key;
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSString *first = [a objectForKey:#"perc"];
NSString *second = [b objectForKey:#"perc"];
return [first compare:second];
the problem is that this key is a numbers, and order is so for example: 1,10, 15, 17, 2, 23, etc.
it does not calculate the magnitude of the number.
how can I do?
Can't you return the comparison result like this,
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
int first = [[a objectForKey:#"perc"] intValue];
int second = [[b objectForKey:#"perc"] intValue];
if ( first < second ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( first > second ) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}
A very short way of #(Deepak Danduprolu)'s answer
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSNumber *first = #([[a objectForKey:#"perc"] intValue]);
NSNumber *second = #([[b objectForKey:#"perc"] intValue]);
return [first compare:second];
}
Make them into NSNumber objects and compare those instead. Either store them as NSNumbers to start with (preferred method) or convert them when comparing (slower).
Converting an NSString to an NSNumber can be done with:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:string];
[f release];
Maybe you can do something like this.
- (void)setPlaces:(NSArray *)places
{
if(_places != places)
{
_places = [places sortedArrayUsingComparator:^(id obj1, id obj2){
return [[obj1 objectForKey:FLICKR_PLACE_NAME] caseInsensitiveCompare:[obj2 objectForKey:FLICKR_PLACE_NAME]];
}];
[self.tableView reloadData];
}
}
I did something that was a cross between yours and the above and it works:
- (void)setPlaces:(NSArray *)places
{
if(_places != places)
{
_places = [places sortedArrayUsingComparator:^(id a, id b){
NSString *first = [a objectForKey:FLICKR_PLACE_NAME];
NSString *second = [b objectForKey:FLICKR_PLACE_NAME];
return [first caseInsensitiveCompare:second];
}
];
if (self.tableView.window) [self.tableView reloadData];
} //if _places
} //set