Jeet Kapadia Jeet Kapadia - 2 months ago 6
Objective-C Question

Getting repeated values when I try to add a dictionary to an array in a loop

This is my code for my test app where I want to show users on the next view controller but when I run and try to retrieve the values it is giving me repeated values. I have used

NSMutableDictionary
and
NSMutableArray
.

#import "DBManager.h"

static DBManager *sharedInstance = nil;
static sqlite3 *database = nil;
static sqlite3_stmt *statement = nil;

@implementation DBManager

-(void) getsaveData:(NSString *)Username
{
const char *dbpath = [newFileAtPath UTF8String];
NSMutableArray *userArray = [[NSMutableArray alloc]init];
if (sqlite3_open(dbpath, &database)==SQLITE_OK) {
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
NSString *getSQL = [NSString stringWithFormat:@"SELECT * FROM USERS WHERE USERNAME != \"%@\"",Username];
const char *retrievestmt = [getSQL UTF8String];
if(sqlite3_prepare_v2(database, retrievestmt, -1, &statement, NULL)==SQLITE_OK)
{
while(sqlite3_step(statement)==SQLITE_ROW)//in this while loop i am getting repeated values
{
NSString *User_ID = [[NSString alloc] initWithUTF8String: (const char *) sqlite3_column_text(statement, 0)];
[userInfo setObject:User_ID forKey:@"ID"];
//[userArray addObject:User_ID];

NSString *User_Email = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
[userInfo setObject:User_Email forKey:@"Email"];
//[userArray addObject:User_Email];

NSString *Password = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
[userInfo setObject:Password forKey:@"Email"];
//[userArray addObject:Password];

NSString *User_name = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 3)];
[userInfo setObject:User_name forKey:@"Username"];
//[userArray addObject:User_name];

NSString *User_Avatar = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)];
[userInfo setObject:User_Avatar forKey:@"Avatar"];
//[userArray addObject:User_Avatar];

[userArray addObject:userInfo];
NSLog(@"userArray %@",userArray);

}
sqlite3_reset(statement);
}
}
}

@end


In the
while
loop I am geeting the error.

I tried multiple times with multiple solutions please help me fix this.

Answer

The problem is simple - you are incorrectly reusing the userInfo dictionary. You need to create a new instance in each loop iteration.

Move the line:

NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];

to just after the while loop line.

You also have many other issues with your code:

  1. You never close the database.
  2. You never finalize the prepared statement.
  3. You needlessly reset the prepared statement.
  4. You have insufficient error checking/logging.
  5. You are not using standard naming conventions.
  6. You are not using standard whitespacing which makes your code harder to read.
  7. You don't use consistent curly brace placement. Pick a style and use it everywhere.
  8. You are using static variables when you should not be.
  9. Never build SQL statements using stringWithFormat:. Properly bind values to the query.
  10. Never use SELECT * in a query. Explicitly list out the columns to ensure you get consistent and expected column values.

Here's is your code updated for these issues:

#import "DBManager.h"

static DBManager *sharedInstance = nil;

@implementation DBManager

- (void)getsaveData:(NSString *)username {
    const char *dbpath = [newFileAtPath UTF8String];
    NSMutableArray *userArray = [[NSMutableArray alloc] init];
    sqlite3 *database;
    if (sqlite3_open(dbpath, &database) == SQLITE_OK) {
        const char *retrievestmt = "SELECT USER_ID, USER_EMAIL, PASSWORD, USER_NAME, USER_AVATAR FROM USERS WHERE USERNAME != ?"; // replace * with actual column names
        sqlite3_stmt *statement;
        if (sqlite3_prepare_v2(database, retrievestmt, -1, &statement, NULL) == SQLITE_OK) {
            sqlite3_bind_text(statement, 1, [username UTF8String], -1, SQLITE_TRANSIENT);
            while (sqlite3_step(statement) == SQLITE_ROW) {
                NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
                NSString *userID = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
                userInfo[@"ID"] = userID;

                NSString *userEmail = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
                userInfo[@"Email"] = userEmail;

                NSString *password = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
                userInfo[@"Password"] = password;

                NSString *username = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
                userInfo[@"Username"] = username;

                NSString *userAvatar = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 4)];
                userInfo[@"Avatar"] = userAvatar;

                [userArray addObject:userInfo];
            }

            sqlite3_finalize(statement);

            NSLog(@"userArray %@",userArray);
        } else {
            NSLog(@"Unable to prepare the statement at %s: %s", retrievestmt, sqlite3_errmsg(database));
        }

        sqlite3_close(database);
    } else {
        NSLog(@"Unable to open the database at %@: %s", dbpath, sqlite3_errmsg(database));
    }
}

@end
Comments