I have an NSArray of names, I want to sort them alphabetically into a UITableView and separate them into sections.
I have a tagged section at the top, being section 0. I want the names sorted aplhabetically to come after that. So all names beginning with A get put into section 1, B into section 2, and so on.
I need to be able to somehow get the number of rows for each section, and then put the objects in each section.
How do I do this?
Here is a method for a category on NSArray to do grouping:
#interface NSArray (Grouping)
- (NSArray*) groupUsingFunction: (id (*)(id, void*)) function context: (void*) context;
#end
#implementation NSArray (Grouping)
- (NSArray*) groupUsingFunction: (id (*)(id, void*)) function context: (void*) context
{
NSArray* groupedArray = nil;
NSMutableDictionary* dictionary = [NSMutableDictionary new];
if (dictionary != nil)
{
for (id item in self)
{
id key = function(item, context);
if (key != nil)
{
NSMutableArray* array = [dictionary objectForKey: key];
if (array == nil) {
array = [NSMutableArray arrayWithObject: item];
if (array != nil) {
[dictionary setObject: array forKey: key];
}
} else {
[array addObject: item];
}
}
}
groupedArray = [NSMutableArray arrayWithArray: [dictionary allValues]];
[dictionary release];
}
return groupedArray;
}
#end
You can use it like this:
id GroupNameByFirstLetter(NSString* object, void* context)
{
return [object substringToIndex: 1];
}
NSInteger SortGroupedNamesByFirstLetter(id left, id right, void* context)
{
return [[left objectAtIndex: 0] characterAtIndex: 0] - [[right objectAtIndex: 0] characterAtIndex: 0];
}
NSMutableArray* names = [NSArray arrayWithObjects: #"Stefan", #"John", #"Alex",
#"Sue", #"Aura", #"Mikki", #"Michael", #"Joe", #"Steve", #"Mac", #"Fred",
#"Faye", #"Paul", nil];
// Group the names and then sort the groups and the contents of the groups.
groupedNames_ = [[names groupUsingFunction: GroupNameByFirstLetter context: nil] retain];
[groupedNames_ sortUsingFunction: SortGroupedNamesByFirstLetter context: nil];
for (NSUInteger i = 0; i < [groupedNames_ count]; i++) {
[[groupedNames_ objectAtIndex: i] sortUsingSelector: #selector(compare:)];
}
I modified St3fans answer to be a bit more modern and work with Blocks instead:
#interface NSArray (Grouping)
- (NSArray*) groupUsingBlock:(NSString* (^)(id object)) block;
#end
- (NSArray*) groupUsingBlock:(NSString* (^)(id object)) block
{
NSArray* groupedArray = nil;
NSMutableDictionary* dictionary = [NSMutableDictionary new];
if (dictionary != nil)
{
for (id item in self)
{
id key = block(item);
if (key != nil)
{
NSMutableArray* array = [dictionary objectForKey: key];
if (array == nil) {
array = [NSMutableArray arrayWithObject: item];
if (array != nil) {
[dictionary setObject: array forKey: key];
}
} else {
[array addObject: item];
}
}
}
groupedArray = [NSMutableArray arrayWithArray: [dictionary allValues]];
[dictionary release];
}
return groupedArray;
}
You can use it like this:
NSArray *grouped = [arrayToGroup groupUsingBlock:^NSString *(id object) {
return [object valueForKey:#"name"];
}];
You should probably create an array of arrays, one for each letter, and store your names that way. While you can use a single array for storage, there's no quick way to do the segmentation you're looking for. Sorting, sure, but not section-ization.
Related
I have a NSMutableDictionary and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString objects
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:#{
#"key1" : #"value1",
#"key2" : #"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(#"%#", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
unique values (as they will become keys)
values conform to NSCopying (same as above)
no value is equal to any key (otherwise colliding names will be lost in the process)
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
Transverses the whole hierarchy of a collection, swapping all the keys
It's fast since it uses recursion and fast enumeration
Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
#end
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:#protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
#end
and...
#interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
#end
#implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
#end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = #{#"1" : #"a",
#"2" : #[ #{ #"5" : #"b" } ],
#"3" : #{#"6" : #"c"},
#"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>" represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption:
No key = value;
Complexity:
No extra space required. Will loop through once and replace all key value pairs.
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];
I have a NSMutableArray and I need to compare the content in it with a NSString..But couldn't get it ?
My array
(
{
Code=Sunday;
},
{
Code=Monday;
},
{
Code=Tuesday;
}
)
I m comparing like this :
BOOL isTheObjectThere = [array containsObject:weekday];
if([arr count]==0 && isTheObjectThere==YES)
{
//do something
}
else
{
//do something
}
Here weekday is NSString whose value=Sunday
But isTheObjectThere is returning NO..
Where Im going wrong?
see this example
NSMutableArray* array = [NSArray arrayWithObjects:#"Sunday", #"Monday", #"Tuesday", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %#", array];
BOOL isTheObjectThere = [predicate evaluateWithObject:#"Sunday"];
NSLog(#"isTheObjectThere %d",isTheObjectThere);
its work fine and return isTheObjectThere 1
The array contains dictionary elements, then retrieve values of key(Code).
NSMutableArray *yourArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in array) {
[yourArray addObject:[dict valueForKey:#"Code"]];
}
BOOL isTheObjectThere = [yourArray containsObject:#"Sunday"];
BOOL isTheObjectThere = [array containsObject:weekday];
will not work, better try the following method.
NSMutableArray* inputArray = [NSArray arrayWithObjects:#"Sunday", #"Monday", #"Tuesday", nil];
for (NSString* item in inputArray)
{
if ([item rangeOfString:#"Sunday"].location != NSNotFound)
{
//do something
}
else
{
//do something;
}
}
NSString *yourString = #"Monday";
for (NSString* item in inputArray){
if ([yourString rangeOfString:item].location != NSNotFound)
//Do something;
else
// Do the other Thing;
}
The question sounds weird but I'm getting an array of dictionaries as parsed result.
Something like this:
parsed content: (
{
"name" = "John";
"lastname" = "Doe";
"foo" = "bar";
}
What would be the suggestion for best way to create an array of values??
Like this?
- (void)flattenDictionary:(NSDictionary *)d intoKeys:(NSMutableArray *)keys andValues:(NSMutableArray *)values {
for (id key in [d allKeys]) {
[keys addObject:key];
[values addObject:[d valueForKey:key]];
}
}
- (void)flattenDictionaries:(NSArray *)arrayOfDictionaries {
NSMutableArray *keys = [NSMutableArray array];
NSMutableArray *values = [NSMutableArray array];
for (NSDictionary *d in arrayOfDictionaries) {
[self flattenDictionary intoKeys:keys andValues:values];
}
NSLog(#"now we have these values %#", values);
NSLog(#"corresponding to these keys %#", keys);
}
You can get the values with:
NSArray *values = dictionary.allValues;
Or, loop through it:
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
NSLog(#"%# = %#", key, object);
}];
To do that loop through them and create an array.
Now I'm trying the following and it works.
- (void)findDictionaryWithValueForKey:(NSString *)name {
for (NSDictionary * set in myArray) {
if ([[set objectForKey:#"title"] isEqualToString:name])
\\do something
}
}
EDIT:
I've added one extra argument to the post of bshirley. Now it looks more flexible.
- (NSDictionary *)findDictionaryWithValue:(NSString*)name forKey:(NSString *)key {
__block BOOL found = NO;
__block NSDictionary *dict = nil;
[self.cardSetsArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dict = (NSDictionary *)obj;
NSString *title = [dict valueForKey:key];
if ([title isEqualToString:name]) {
found = YES;
*stop = YES;
}
}];
if (found) {
return dict;
} else {
return nil;
}
}
Here's one possible implementation using newer API. (I also modified the method to actually return the value). Provided mostly to demonstrate that API. The assumption is that the title is unique to one dictionary within your array.
- (NSDictionary *)findDictionaryWithValueForKey:(NSString *)name {
// ivar: NSArray *myArray;
__block BOOL found = NO;
__block NSDictionary *dict = nil;
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dict = (NSDictionary *)obj;
NSString *title = [dict valueForKey:#"title"];
if ([title isEqualToString:name]) {
found = YES;
*stop = YES;
}
}];
if (found) {
return dict;
} else {
return nil;
}
}
Use filteredArrayUsingPredicate: method of the array to get all the dictionaries that satisfy your requirement.
NSPredicate * predicate = [NSPredicate predicateWithFormat:#" title MATCHES[cd] %#", name];
NSArray * matches = [myArray filteredArrayUsingPredicate:predicate];
Now matches is the array of dictionaries that have the title key equal to name.
- (void)findDictionaryWithValueForKey:(NSString)name {
for (NSDictionary * set in myArray) {
NSString *s=[NSString stringWithFormat:#"%#",[set objectForKey:#"title"]];
if ([s isEqualToString:name])
\\do something
}
OR
if (s == name])
\\do something
}
}
I will also suggest this way,it would be better if you use a break statement,
- (void)findDictionaryWithValueForKey:(NSString)name {
for (NSDictionary * set in myArray) {
if ([[set objectForKey:#"title"] isEqualToString:name])
\\do something
break;
}
}
As per the NSArray documentation,
valueForKey:
Returns an array containing the results of invoking valueForKey: using key on each of the array's objects.
- (id)valueForKey:(NSString *)key
Parameters
key
The key to retrieve.
Return Value
The value of the retrieved key.
Discussion
The returned array contains NSNull elements for each object that returns nil.
Availability
* Available in Mac OS X v10.3 and later.
EDIT:
try this,
[myArray valueForKey:#"name"];
//this will return array of values, but this actually differ from what to want
I need to sort a NSDictionary of dictionaries. It looks like:
{//dictionary
RU = "110.1"; //key and value
SG = "150.2"; //key and value
US = "50.3"; //key and value
}
Result need to be like:
{//dictionary
SG = "150.2"; //key and value
RU = "110.1"; //key and value
US = "50.3"; //key and value
}
I am trying this:
#implementation NSMutableDictionary (sorting)
-(NSMutableDictionary*)sortDictionary
{
NSArray *allKeys = [self allKeys];
NSMutableArray *allValues = [NSMutableArray array];
NSMutableArray *sortValues= [NSMutableArray array];
NSMutableArray *sortKeys= [NSMutableArray array];
for(int i=0;i<[[self allValues] count];i++)
{
[allValues addObject:[NSNumber numberWithFloat:[[[self allValues] objectAtIndex:i] floatValue]]];
}
[sortValues addObjectsFromArray:allValues];
[sortKeys addObjectsFromArray:[self allKeys]];
[sortValues sortUsingDescriptors:[NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"floatValue" ascending:NO] autorelease]]];
for(int i=0;i<[sortValues count];i++)
{
[sortKeys replaceObjectAtIndex:i withObject:[allKeys objectAtIndex:[allValues indexOfObject:[sortValues objectAtIndex:i]]]];
[allValues replaceObjectAtIndex:[allValues indexOfObject:[sortValues objectAtIndex:i]] withObject:[NSNull null]];
}
NSLog(#"%#", sortKeys);
NSLog(#"%#", sortValues);
NSLog(#"%#", [NSMutableDictionary dictionaryWithObjects:sortValues forKeys:sortKeys]);
return [NSMutableDictionary dictionaryWithObjects:sortValues forKeys:sortKeys];
}
#end
This is the result of NSLog:
1)
{
SG,
RU,
US
}
2)
{
150.2,
110.1,
50.3
}
3)
{
RU = "110.1";
SG = "150.2";
US = "50.3";
}
Why is this happening? Can you help me with this problem?
NSDictionary are unsorted by nature. The order of the objects as retrieved by allKeys and allValues will always be undetermined. Even if you reverse engineer the order it may still change in the next system update.
There is however more powerful alternatives to allKeys that are used to retrieve the keys in a defined and predictable order:
keysSortedByValueUsingSelector: - Useful for sorting in ascending order according to the compare: method of the value objects.
keysSortedByValueUsingComparator: - New in iOS 4, use a block to do the sort inline.
WOW. Thanx, PeyloW! It's what i needed! I also find this code and it helps me to reorder results:
#implementation NSString (numericComparison)
- (NSComparisonResult) floatCompare:(NSString *) other
{
float myValue = [self floatValue];
float otherValue = [other floatValue];
if (myValue == otherValue) return NSOrderedSame;
return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}
- (NSComparisonResult) intCompare:(NSString *) other
{
int myValue = [self intValue];
int otherValue = [other intValue];
if (myValue == otherValue) return NSOrderedSame;
return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}
#end
a NSDictionary is not ordened, so it doens't matter in what order you construct a NSDIctionary.
a NSArray is ordened. If you want to have the NSDictionary ordened in memory, you should somehow make a NSArray of key value pairs. You can also return two NSArrays with corresponding indeces.
If you only want to iterate over the elements way, you can iterate over a sorted array of keys (this is what koregan suggests).