NSMutableArray addObject using value and key - objective-c

I found some codes like this:
for (int i = 0 ; i < pinyin_array.count; i++) {
[pinyins_arr addObject:#{k_PINYIN : pinyin_array[i]}];
}
where pinyins_arr is a NSMutableArray and Kk_PINYIN is a NSString. I am not sure if this is correct or not. I think it should be a NSMutableDictionary to add object of key and value pair. If this is true, the same key will be used for multiple values, what is the final array like?

The #{} syntax is shorthand for creating an NSDictionary. The second line could be verbosely re-written as:
[pinyins_arr addObject:[NSDictionary dictionaryWithObject:pinyin_array[i]] forKey:k_PINYIN]
You are adding NSDictionary objects to your NSMutableArray, so you will have an array of dictionaries which all have the same key. It is correct, if this is what you want.

To say if it's correct or not is not black and white.
It's correct in that it's legal to store an array of dictionaries like this.
It's not correct as there is no need to use an array of dictionaries at all, as there is no variation in the dictionary keys; you may as well just store an array of the values, which is what pinyin_array is anyway.

Try this logic
NSObject *collectionIndexString = indexPath;
NSMutableDictionary *tempDict = [myNSMutableArray[row] mutableCopy];
tempDict[#"collectionIndex"] = collectionIndexString;// Setting Key and Value
[myNSMutableArray replaceObjectAtIndex:row withObject:tempDict];

Related

Is there a way to get all the first objects of an array of arrays with KVC?

Say I have an array of arrays like:
NSArray *array = #[#[#1, #2], #[#3, #4], #[#5, #6]];
Is there a KVC key path that would give me #[#1, #3, #5] ?
To my surprise, there is, although it relies on some undocumented behavior.
The documentation for NSArray's override of -valueForKey: says it builds a new array by applying -valueForKey: to each element with the provided key and returns that. It does not suggest that any keys are special.
However, NSDictionary's override does say that keys starting with # are treated specially. Instead of looking at the contents of the dictionary, it looks at the properties of the dictionary itself for one matching the key stripped of the leading #. So, you could use [someDict valueForKey:#"#count"] to get the count of objects in the dictionary.
As it turns out, NSArray actually respects the same convention. You can use [array valueForKey:#"#firstObject"] to get #[#[#1, #2]] in your example. Now the question is, how do you get that to apply to the inner arrays, not the outer array.
First, what happens if you just call [array valueForKey:#"firstObject"] without the # on the key? The outer array calls [innerArray valueForKey:#"firstObject"] on each of the inner arrays. Each inner array, in turn, tries to call [element valueForKey:#"firstObject"] on each of its elements. However, its elements are NSNumber objects which aren't KVC-compliant for the key "firstObject". So that blows up.
But, KVC supports collection operators with -valueForKeyPath: (note the "Path" on the end of that). One operator is #unionOfObjects. This applies the part of the path to the right of the operator to the elements of the array and then returns an array of the results. This is similar to the case where -valueForKey: propagates to the inner arrays, except you get to specify a different key for the inner arrays.
So, combining these mechanisms, you can do: [array valueForKeyPath:#"#unionOfObjects.#firstObject"] to get #[#1, #3, #5]. The outer array applies [innerArray valueForKeyPath:#"#firstObject"] to each inner array. Because of the leading #, that does not propagate to the elements of the inner arrays. It just gets the firstObject of the inner arrays. Then the outer array combines those into a result array.
KVC key path work only when Object is confirming Key Value coding compliance . means object , scaler value should have setter and getter along the key name. In you structure there is no key for index path having getter and setter. So I have created custom getter method for satisfy NSKeyValueCoding for inner object index path. this will work for each index of inner array.
#interface NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath;
#end
#implementation NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath
{
NSMutableArray * values = [NSMutableArray new];
for (id obj in self) {
if ([obj isKindOfClass:[NSArray class]]) {
#try {
NSUInteger index = [keyPath integerValue];
id value = nil;
if (index < [(NSArray *)obj count]) {
value = [obj objectAtIndex:index];
if(value && value!=[NSNull null] )
{
[values addObject:value];
}
}
}
#catch (id) {}
}
}
return [NSArray arrayWithArray:values];
}
#end
In VC :
self.myArray = #[#[#1, #2], #[#3, #4], #[#5, #6]];
NSArray *testArray =[self.myArray unionOfObjectsForKeyPath:#"0"];
NSLog(#"test array : %#", testArray);

Objective C Array of Array of Strings

I'm trying to make an array of array of strings so that I can eventually pull out something like ArrayOfArrays[0][1] = "hi".
NSString *ArrayOne[] = {#"hello", #"hi"};
NSString *ArrayTwo[] = {#"goodbye", #"bye"};
NSArray *ArrayOfArrays[] = {#[*ArrayOne, *ArrayTwo]};
However when I try to do this, I get an error: Initializer element is not a compile-time constant.
I've read that this is because I'm creating an array with dynamic values, though it should be static. Not sure how to work around this.
Any advice on making an array of array of strings?
Use NSArray, or rather NSMutableArray if you want to modify it after creation:
NSMutableArray *arrayOne = [#[#"hello", #"hi"] mutableCopy];
NSMutableArray *arrayTwo = [#[#"goodbye", #"bye"] mutableCopy];
NSMutableArray *arrayOfArrays = [#[arrayOne, arrayTwo] mutableCopy];
There are other ways to initialise it, but this is the only way that allows you to use Objective-C literal syntax.
You cannot store plain ol' C arrays within an Objective-C collection class as your code attempts to do.
You wrote:
it should be static
if this is what you want then your use of C arrays is quite valid, you just got the syntax wrong. You can use:
NSString *arrayOfArrays[][2] =
{ {#"hello", #"hi"},
{#"goodbye", #"bye"},
};
Important: The 2 is the number of elements in the inner array, you do not change it when adding further pairs.
This will give you a compile-time static array.
If what you are making is a map from one word to another you might be better off with a dictionary, e.g.:
NSDictionary *wordMap =
#{ #"hello" : #"hi",
#"goodbye" : #"bye"
};
and accessing an element becomes:
wordMap[#"hello"];
Note: the dictionary "constant" here is actually executed code; the C array version can appear as a global or local initialiser, while the dictionary initialisation must be done in a method/function - but it can assign to a global.
HTH
NSArray *array = #[
#[[ #"hello", #"hi" ] mutableCopy],
#[[ #"goodbye", #"bye" ] mutableCopy],
];
NSLog(#"%# is short for %#", array[0][1], array[0][0]);
Output: hi is short for hello

Create Instance variables at runtime

I want to create instance variables dynamically at runtime, and I want to add these variables to a category. The number of the instance variables may change based on the configuration/properties file which I am using for defining them.
Any ideas??
Use Associative References - this is tricky, but that is the mechanism invented specifically for your use case.
Here is an example from the link above: first, you define a reference and add it to your object using objc_setAssociatedObject; then you can retrieve the value back by calling objc_getAssociatedObject.
static char overviewKey;
NSArray *array = [[NSArray alloc] initWithObjects:# "One", #"Two", #"Three", nil];
NSString *overview = [[NSString alloc] initWithFormat:#"%#", #"First three numbers"];
objc_setAssociatedObject (
array,
&overviewKey,
overview,
OBJC_ASSOCIATION_RETAIN
);
[overview release];
NSString *associatedObject = (NSString *) objc_getAssociatedObject (array, &overviewKey);
NSLog(#"associatedObject: %#", associatedObject);
objc_setAssociatedObject (
array,
&overviewKey,
nil,
OBJC_ASSOCIATION_ASSIGN
);
[array release];
I'd be inclined to just use a NSMutableDictionary (see NSMutableDictionary Class Reference). Thus, you would have an ivar:
NSMutableDictionary *dictionary;
You'd then initialize it:
dictionary = [NSMutableDictionary dictionary];
You can then save values to it dynamically in code, e.g.:
dictionary[#"name"] = #"Rob";
dictionary[#"age"] = #29;
// etc.
Or, if you are reading from a file and don't know what the names of the keys are going to be, you can do this programmatically, e.g.:
NSString *key = ... // your app will read the name of the field from the text file
id value = ... // your app will read the value of the field from the text file
dictionary[key] = value; // this saves that value for that key in the dictionary
And if you're using an older version of Xcode (before 4.5), the syntax is:
[dictionary setObject:value forKey:key];
Depends on exactly what you want to do, the question is vague but if you want to have several objects or several integers or so on, arrays are the way to go. Say you have a plist with a list of 100 numbers. You can do something sort of like this:
NSArray * array = [NSArray arrayWithContentsOfFile:filePath];
// filePath is the path to the plist file with all of the numbers stored in it as an array
That will give you an array of NSNumbers, you can then turn that into an array of just ints if you want like this;
int intArray [[array count]];
for (int i = 0; i < [array count]; i++) {
intArray[i] = [((NSNumber *)[array objectAtIndex:i]) intValue];
}
Whenever you want to get an integer from a certain position, lets say you want to look at the 5th integer, you would do this:
int myNewInt = intArray[4];
// intArray[0] is the first position so [4] would be the fifth
Just look into using a plist for pulling data, it will them be really easy to create arrays of custom objects or variables in your code by parsing the plist.

objective-c changing array contents inside for loop

I have an issue that (I think) might have to do with scope, but I'm not sure. I'm trying to do something that I think should be simple, but I am getting a strange result, and I could truly use some advice. I would say I'm an early-objective-c programmer, but not a complete newb.
I have written a function in objective-c that I would like to use to change the key-names in a mutable array of mutable dictionary objects. So, I want to pass in a mutable array of mutable dictionary objects, and return the same mutable array with the same dictionary objects, but with some of the key-names changed. Make sense?
I have tried several log statements in this code, which seem to indicate that everything I'm doing is working, except when the for loop is finished executing (when I try to test the values in the temp array), the array appears to contain only the LAST element in the source array, repeated [source count] times. Normally, this would lead me to believe I'm not writing the new values correctly, or not reading them correctly, or even that my NSLog statements aren't showing me what I think they are. But might this be because of scope? Does the array not retain its changes outside of the for loop?
I have put a fair amount of time into this function, and I have exhausted my bag of tricks. Can anyone help out?
-(NSMutableArray *)renameKeysIn:(NSMutableArray*)source {
/*
// Pre:
// The source array is an array of dictionary items.
// This method renames some of the keys in the dictionary elements, to make sorting easier later.
// - "source" is input, method returns a mutable array
*/
// copy of the source array
NSMutableArray *temp = [source mutableCopy];
// a temporary dictionary object:
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
// These arrays are the old field names and the new names
NSMutableArray *originalField = [NSMutableArray arrayWithObjects:#"text", #"created_at",nil];
NSMutableArray *replacedField = [NSMutableArray arrayWithObjects:#"title", #"pubDate", nil];
// loop through the whole array
for (int x =0; x<[temp count]; x++) {
// set the temp dictionary to current element
[dict setDictionary:[temp objectAtIndex:x]];
// loop through the number of keys (fields) we want to replace (created_at, text)... defined in the "originalField" array
for (int i=0; i<[originalField count]; i++)
{
// look through the NSDictionary item (fields in the key list)
// if a key name in the dictionary matches one of the ones to be replaced, then replace it with the new one
if ([dict objectForKey:[originalField objectAtIndex:i]] != nil) {
// add a new key/val pair: the new key *name*, and the old key *value*
[dict setObject:[dict objectForKey:[originalField objectAtIndex:i]]
forKey:[replacedField objectAtIndex:i]];
// remove the old key/value pair
[dict removeObjectForKey:[originalField objectAtIndex:i]];
}// end if dictionary item not null
}// end loop through keys (created_at, text)
[temp replaceObjectAtIndex:x withObject:dict];
}// end loop through array
// check array contents
for (int a=0; a<[temp count]; a++){
NSLog(#"Temp contents: ############ %#",[[temp objectAtIndex:a] objectForKey:#"pubDate"]);
}
return temp;
} // end METHOD
I think the issue is on the line with:
[dict setDictionary:[temp objectAtIndex:x]];
Since these things are almost all working in pointers (instead of copying contents), every element of your temp array will point to the dict dictionary, which is set to be whatever the latest key's dictionary is. I think setting the actual pointer will fix the issue.
dict = [temp objectAtIndex:x];

iPhone, How can I store a value and a key and lookup by key and index?

I've tried NSMutableDictionary however I don't seem to be able to get an object by index.
Any ideas?
EDIT:
I've trying to create a uitableview sections object, which will store the header titles and be able to increment a counter for the rows. I need to be able to get the counter by index, counter value by title value.
Simplest way is to use 2 collections: dictionary for section infos (row numbers, countries etc) and array for section titles.
NSMutableDictionary *sectionInfos;
NSMutableArray *sectionTitles;
When you need a section info by sectionTitle:
NSDictionary *info = [sectionInfos objectForKey:sectionTitle];
int rowsCount = ((NSArray *)[info objectForKey:#"Countries"]).count;
When you need a section info by sectionIndex:
NSString *title = [sectionTitles objectAtIndex:sectionIndex];
NSDictionary *info = [sectionInfos objectForKey:title];
int rowsCount = ((NSArray *)[info objectForKey:#"Countries"]).count;
When you add a section, add sections info:
[sectionInfos setObject:info forKey:sectionTitle];
and a title to array, so infos and titles will be in sync.
[sectionTitles addObject:sectionTitle];
UPDATE: if the only info needed for section is number of rows:
UPDATE2: added types.
NSMutableDictionary *sectionRowCounts;
NSMutableArray *sectionTitles;
Rows count by sectionTitle:
int rowCount = [[sectionRowCounts objectForKey:sectionTitle] intValue];
Rows count by sectionIndex:
NSString *title = [sectionTitles objectAtIndex:sectionIndex];
int rowCount = [[sectionRowCounts objectForKey:title] intValue];
Adding a section:
[sectionRowCounts setObject:[NSNumber numberWithInt:rowCount] forKey:sectionTitle];
[sectionTitles addObject:sectionTitle];
Dictionaries are not ordered; therefore the objects in them do not have an index.
http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSDictionary_Class/Reference/Reference.html#//apple_ref/occ/cl/NSDictionary
You need to use the key to retrieve a particular object from a dictionary. If you need to have the objects in a specific order, then you would probably use NSArray instead.
UPDATE
In your edit, you don't show what tableSectionArray is, but it looks like it's a dictionary (which makes it poorly named). You should use an NSArray, not an NSDictionary, to store what you want. If you need more than one value to be stored, then store an object that contains the values you need. Create a class that has the required values as properties; or, if appropriate, add NSDictionary objects to your array. (Based on how you are trying to assign an element from tableSectionArray, it looks like you do want it to contain dictionaries.) But you need the tableSectionArray itself to be an NSArray.
Yes, keep trying with NSMutableDictionary. It's the data structure you need for that. Can you post your code to see why it's not returning the value you expect?
Example:
NSString *yourvalue = #"Hello!";
NSMutableDictionary *d;
[d setValue:yourvalue forKey:#"yourkey"];
NSString *retrievedvalue = [d valueForKey:#"yourkey"];
// you should get value here