I have 2 arrays that receive information from a database. One, called json, contains posts with user id's and the other, called userJson, contains all the users with their id's. If the the user id from json equals a user id from userJson, I would like to populate a 3rd array with that users username. I am using the following code:
NSDictionary *info = [json objectAtIndex:indexPath.row];
NSDictionary *userInfo = [userArray objectAtIndex:0];
for (int i=0; i<[json count]; i++) {
if ([[userInfo objectForKey:#"user_id"] isEqualToString:[info objectForKey:#"user_id"]]) {
[usernameArray addObject:[userInfo objectForKey:#"username"]];
}
}
NSLog(#"%#", usernameArray);
usernameArray is the 3rd array that should contain the usernames. When I print it, it prints out null various times.
You need to first add this:
usernameArray = [[NSMutableArray alloc]init];
Then change your loop to the following:
NSDictionary *info = [json objectAtIndex:indexPath.row];
NSDictionary *userInfo;
for (int i=0; i<[userArray count]; i++) {
userInfo = [userArray objectAtIndex:i];
if ([[userInfo objectForKey:#"user_id"] isEqualToString:[info objectForKey:#"user_id"]]) {
[usernameArray addObject:[userInfo objectForKey:#"username"]];
}
}
Related
I have an NSmutable array and I am adding some strings present in the C array to it. By using this method
if (!self.arrayOfVariableNames) {
self.arrayOfVariableNames = [[NSMutableArray alloc] init];
for (int i = 0; i< cols; i++) {
[self.arrayOfVariableNames addObject:[NSString stringWithCString:cArrayOfVariableNames[i] encoding:NSUTF8StringEncoding ]];
}
}
else{
[self.arrayOfVariableNames removeAllObjects];
for (int i = 0; i< cols; i++) {
[self.arrayOfVariableNames addObject:[NSString stringWithCString:cArrayOfVariableNames[i] encoding:NSUTF8StringEncoding ]];
}
}
Does this method ensure that the objects in the NSmutableArray won't be deallocated when the C array is taken out of memory?
if this array arrayOfVariableNames is becoming Null, then the problem is with the initialisation of the array. Please try to use Lazy loading by doing this:
- (NSArray*)arrayOfVariableNames {
if (!_arrayOfVariableNames) {
_arrayOfVariableNames = [[NSMutableArray alloc] init]; //initialise the array if needed
}
return _arrayOfVariableNames; //else return the already initialized array
}
and please comment out this line in your code: self.arrayOfVariableNames = [[NSMutableArray alloc] init];
****EDIT****
Please find the update code in https://docs.google.com/file/d/0BybTW7Dwp2_vdHhQN1p1UzExdTA/edit?pli=1. Have a look at it.
Yes. NSArray retains anything in it.
But you should stop chaining your NSString creation and instead creat a string a line before adding it to the array. Then check for nil.
Only add it to the array if it is not nil.
Do code defensively.
arrayOfVariableNames will not change when the C array get deallocated.
Make sure that your arrayOfVariableNames variable is strong.
#property (nonatomic, strong) NSMutableArray *arrayOfVariableNames;
if (!self.arrayOfVariableNames)
{
self.arrayOfVariableNames = [[NSMutableArray alloc] init];
}
else
{
[self.arrayOfVariableNames removeAllObjects];
}
for (int i = 0; i< cols; i++)
{
NSString *tempString = [NSString stringWithCString:cArrayOfVariableNames[i] encoding:NSUTF8StringEncoding];
if([tempString length] > 0)
{
[self.arrayOfVariableNames addObject:tempString];
}
else
{
NSLog(#"string is empty");
}
}
I define a NSMutableArray *nameArray in .h file. And add strings using this nameArray in a for loop in .m file like below:
for (int i=0; i<[data count]; i++) {
NSDictionary* place = [data objectAtIndex:i];
NSString *name=[place objectForKey:#"name"];
_nameArray = [[NSMutableArray alloc] init];
[_nameArray addObject:name];
}
NSLog(#"After loop, Name is %#", _nameArray);
Actually, it should have several names in this array. But why it only output one name which was added in the array last?
You must create your array outside the loop, that is your problem.
// Init array
_nameArray = [[NSMutableArray alloc] init];
// Fill array
for (int i=0; i<[data count]; i++) {
NSDictionary* place = [data objectAtIndex:i];
NSString *name=[place objectForKey:#"name"];
[_nameArray addObject:name];
}
// Log array
NSLog(#"After loop, Name is %#", _nameArray);
All of your objects are added to an own array and only the last array will not be overwritten and is therefore in the log.
What I am trying to achieve, is to convert a csv file into an array of custom objects, however, my attempts at this seem to result in all of the objects in the array being returned as the same object (the last object in the array).
Before I explain further, here is the code:
- (NSArray *)arrayFromCSVFileName:(NSString *)csvFileName fileType:(NSString *)fileType {
// Convert the file into an NSData object
NSString *studentFilePath = [[NSBundle mainBundle] pathForResource:csvFileName ofType:fileType];
NSData *studentData = [NSData dataWithContentsOfFile:studentFilePath];
// Convert the NSData into an NSString
NSString *csvString = [[NSString alloc] initWithData:studentData encoding:NSUTF8StringEncoding];
// Split each record (line) in the csvDataString into an individual array element (split on the newline character \n)
NSArray *csvArray = [csvString componentsSeparatedByString:#"\n"];
// Create an array to hold the parsed CSV data
NSMutableArray *parsedCSVArray = [[NSMutableArray alloc] init];
NSMutableArray *elementArray = [[NSMutableArray alloc] init];
CGSElement *elementToAdd = [[CGSElement alloc] init];
// Loop through each line of the file
for (int i = 0; i < [csvArray count]; i++) {
// Get a reference to this record (line) as a string, and remove any extranous new lines or alike
NSString *csvRecordString = [[csvArray objectAtIndex:i] stringByReplacingOccurrencesOfString:#"\r" withString:#""];
// Split the line by the comma delimeter
NSArray *csvRecordArray = [csvRecordString componentsSeparatedByString:#","];
// Check that there are actually fields (i.e. this is not a blank line)
if ( ([csvRecordArray count] > 0) && ([[csvRecordArray objectAtIndex:0] length] > 0) ) {
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
[elementArray addObject:elementToAdd];
}
}
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
// Return the parsed array
return elementArray;
}
The custom object in question is the CGSElement object, which I am attempting to fill the elementArray with. However, my debug code (the following section of code):
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
Is resulting, rather than in the return of all of the correct element names, it is returning the last element (to put this in context, ununoctium), 118 times.
After some testing, I can safely say that up until after this point:
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
All of the elements are being correctly defined, rather than just the same element over and over.
Needless to say, I'm stumped as to why it would be returning the same object over and over. Any help would be appreciated.
This line:
CGSElement *elementToAdd = [[CGSElement alloc] init];
Should be inside your loop, just before you try to edit the object and add it to the array. Currently you are repeatedly mutating the same object instead of creating new objects for each record.
You add the same entity all the time. It is crated once before the loop and within the loop it values are changed again and angan and it is added to the array. Naturally all items in the aray carry the same values because it is the same object.
If you want then change the array with an NSSet. To a set an object can only added once and you will end up with a set of 1. That is not the solution of couse, it would just visualize what is happening.
To solve it move this line
CGSElement *elementToAdd = [[CGSElement alloc] init];
to the beginning of the body of the for i loop, so that a new instance is created for every iteration and therefore for every index of the array.
How can I save the string that match from one NSArray with one index difference in NSMutableArray?
For example, there are three "apple", four "pineapple", six "banana", two "cocoa" and the rest of words dont have duplicate(s) in the nsarray, i would like to know if the nsarray has at least two same words. If yes, I would like to save "apple", "pineapple, "banana" and "cocoa" once in nsmutablearray. If there are other alike words, I would like to add them to namutablearray too.
My code (which still doesn't work properly);
NSArray *noWords = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"words" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL]
componentsSeparatedByString:#"\n"]];
NSUInteger scount = [noWords count];
int ii = 0;
NSString *stringline;
for (ii; ii < scount; ii++)
{
stringline = [noWords objectAtIndex:ii];
NSLog(#"stringline : %# ", stringline);
}
int i = 1;
NSString *line;
for (i ; i < 10; i++)
{
line = [noWords objectAtIndex:i];
NSLog (#"line : %# ", line);
NSMutableArray *douwords = [NSMutableArray array];
if ([stringline isEqualToString:line])
{
NSString *newword;
for (newword in douwords)
{
[douwords addObject:newword];
NSLog (#"detected! %# ", douwords);
}
}
}
Here's a solution using two sets:
- (NSArray *)getDuplicates:(NSArray *)words
{
NSMutableSet *dups = [NSMutableSet set],
*seen = [NSMutableSet set];
for (NSString *word in words) {
if ([seen containsObject:word]) {
[dups addObject:word];
}
[seen addObject:word];
}
return [dups allObjects];
}
Assuming NSSet uses hash tables behind the scenes (which I'm betting it does), this is going to be faster than the previously suggested O(n^2) solution.
Here's something off the top of my head:
NSMutableSet* duplicates = [NSMutableSet set];
NSArray* words = [NSArray arrayWithObjects:#"Apple", #"Apple", #"Orange", #"Apple", #"Orange", #"Pear", nil];
[words enumerateObjectsUsingBlock:^(NSString* str, NSUInteger idx, BOOL *stop) {
for (int i = idx + 1; i < words.count; i++) {
if ([str isEqualToString:[words objectAtIndex:i]]) {
[duplicates addObject:str];
break;
}
}
}];
NSLog(#"Dups: %#", [duplicates allObjects]); // Prints "Apple" and "Orange"
The use of an NSSet, as opposed to an NSArray, ensures strings are not added more than once. Obviously, there are optimizations that could be done, but it should be a good starting point.
I assume that you want to count appearances of words in your array and output those with a count of more than one. A basic and verbose way to do that would be:
// Make an array of words - some duplicates
NSArray *wordList = [[NSArray alloc] initWithObjects:
#"Apple", #"Banana", #"Pencil",
#"Steve Jobs", #"Kandahar",
#"Apple", #"Banana", #"Apple",
#"Pear", #"Pear", nil];
// Make an mutable dictionary - the key will be a word from the list
// and the value will be a number representing the number of times the
// word appears in the original array. It starts off empty.
NSMutableDictionary *wordCount = [[NSMutableDictionary alloc] init];
// In turn, take each word in the word list...
for (NSString *s in wordList) {
int count = 1;
// If the word is already in the dictionary
if([wordCount objectForKey:s]) {
// Increse the count by one
count = [[wordCount objectForKey:s] intValue] + 1;
}
// Save the word count in the dictionary
[wordCount setObject:[NSNumber numberWithInt:count] forKey:s];
}
// For each word...
for (NSString *s in [wordCount keysOfEntriesPassingTest:
^(id key, id obj, BOOL *stop) {
if ([obj intValue] > 1) return YES; else return NO;
}]) {
// print the word and the final count
NSLog(#"%2d %#", [[wordCount objectForKey:s] intValue], s);
}
The output would be:
3 Apple
2 Pear
2 Banana
I have NSTableView with two columns, and i wanna fill them entries from SQLite database.
this method is make query for table
-(void)getPersons
{
NSString *file = [[NSBundle mainBundle] pathForResource:#"persons" ofType:#"db"];
sqlite3 *database = NULL;
if (sqlite3_open([file UTF8String], &database)==SQLITE_OK)
{
NSLog(#"i open database");
sqlite3_exec(database, "select name,age from persons", MyCallback, persons, NULL);
}
sqlite3_close(database);
}
the problem appers in MyCallback method:
static int MyCallback(void *context, int count, char **values, char **columns)
{
NSMutableArray *persons = (NSMutableArray *)context;
for (int i=0; i < count; i++) {
NSString *columnname = [NSString stringWithUTF8String:columns[i]];
const char *nameCString = values[i];
if ([columnname isEqualTo:#"name"]) {
[persons addObject:[NSString stringWithUTF8String:nameCString]];
}
else {
[ages addObject:[NSString stringWithUTF8String:nameCString]];
}
}
return SQLITE_OK;
}
how can i write "age" entries into NSMutableArray *ages, if i can assign *context just for one NSMutableArray? (in this code, this is NSMutableArray *person)
Surely i should create a separate method for get age entries?
thanks.
In many simple cases like the one in your question it is convinient use most natural representation of tables in objective-c: array of dictionaries. Table is indexed collection of records - so it is an array, and each record is collection of key-value pairs - so it is a dictionary. And you need not change you code when you change column names or even number of colums.
...
NSMutableArray *allRecords= (NSMutableArray *)context;
NSString *columnName;
NSString *columnValue;
NSMutableDictionary * record = [NSMutableDictionary dictionary];
for (int i=0; i < count; i++) {
columnName = [NSString stringWithUTF8String:columns[i]];
if (values[i]){
columnValue = [NSString stringWithUTF8String:values[i]];
}
else{
columnValue = #"";
}
[record setObject:columnValue forKey:columnName];
}
[allRecords addObject:record];
...