In my application you can import data through a tab separated values file. I don't have any challenge until I parse "locations" that have multiple items attached to them. If you scroll to the very bottom of the second method you can see how I create a relationship between items and the locations that contain them inside Core Data. The problem occurs when I parse past column 31 in a location. It doesn't attach those items to the location. So my question is this; is there a limit to columns in the NSArray that is parsed by CHCSVParser? If not, what would cause this limiting to 31 columns?
I've posted the two methods that I encounter the bug with below.
+ (void) importDatabaseTSVURL:(NSURL*)url {
// First check if there is already a database. If so, stop import.
if ([XSELLocation locations].count > 0) return;
if ([XSELItem items].count > 0) return;
if ([XSELVendor vendors].count > 0) return;
NSError *error;
NSArray *array = [NSArray arrayWithContentsOfDelimitedURL:url options:CHCSVParserOptionsSanitizesFields delimiter:'\t' error:&error];
if ([[[array firstObject] firstObject] isEqualToString:#"XSELINVENTORYTSV"]) {
for (NSArray *row in array) {
[XSELSettings parseImportDataRow:row];
}
}
}
+ (void) parseImportDataRow:(NSArray*)array {
// Create logic to seperate data entered next
static NSString *operation = #"none";
if ([array.firstObject isEqualToString:#"ITEMLIST"]) {
operation = #"items";
return;
}
else if ([array.firstObject isEqualToString:#"LOCATIONLIST"]) {
operation = #"locations";
return;
}
else if ([array.firstObject isEqualToString:#"VENDORLIST"]) {
operation = #"vendors";
return;
}
else if ([array.firstObject isEqualToString:#"ENDLIST"]) { // Create database, relate objects, and clean up the data
operation = #"none";
return;
}
// Parse rows to the correct array.
if ([operation isEqualToString:#"vendors"]) {
NSLog(#"adding vendor");
XSELVendor *vendor = [XSELVendor addVendor];
vendor.vendorID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextVendorID];
vendor.name = [array objectAtIndex:1];
vendor.contactID = [array objectAtIndex:2];
}
else if ([operation isEqualToString:#"items"]) {
NSLog(#"adding item");
XSELItem *item = [XSELItem addItem];
item.itemID = [NSNumber numberWithDouble:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextItemID];
item.name = [array objectAtIndex:1];
item.smallPackageName = [array objectAtIndex:2];
item.bigPackageName = [array objectAtIndex:3];
item.smallPerBig = [NSNumber numberWithDouble:[[array objectAtIndex:4] integerValue]];
item.buildTo = [NSNumber numberWithDouble:[[array objectAtIndex:5] integerValue]];
item.price = [NSNumber numberWithDouble:[[array objectAtIndex:6] integerValue]];
// Relate preferred vendor to item
for (XSELVendor *vendor in [XSELVendor vendors]) {
if ([vendor.vendorID.stringValue isEqualToString:[array objectAtIndex:7]]) {
item.preferredVendor = vendor;
break;
}
}
}
else if ([operation isEqualToString:#"locations"]) {
NSLog(#"adding location");
XSELLocation *location = [XSELLocation addLocation:[array objectAtIndex:1]];
location.locationID = [NSNumber numberWithInteger:[[array objectAtIndex:0] integerValue]];
[XSELSettings nextLocationID];
location.position = [NSNumber numberWithInteger:[[array objectAtIndex:2] integerValue]];
// Relate location with items
unsigned long itemsRelatedCount = array.count - 3;
NSLog(#"\n\nitemsRelated: %lu\n\n", itemsRelatedCount);
NSMutableOrderedSet *items = [NSMutableOrderedSet orderedSet];
for (int i = 0; i < itemsRelatedCount; i++) {
NSString *itemID = [array objectAtIndex:i];
for (XSELItem *item in [XSELItem items]) {
if ([item.itemID.stringValue isEqualToString:itemID]) {
[items addObject:item];
break;
}
}
}
location.items = items;
}
}
Related
I need to show a grouped tableview from the below data. I need to categorise the below array based on "account_type".
For Eg: I need to show Table Section Heading "Savings" and list all savings type accounts, then similarly get Unique account types and gave that as section header and account numbers in table rows. I am able to get section headers using NSSet, but how to get row counts and display it in a UITableView.
<__NSArrayM 0x7f8ef1e8b790>(
{
"account_no" = 123;
"account_type" = Savings;
},
{
"account_no" = 123456;
"account_type" = Savings;
},
{
"account_no" = 00000316;
"account_type" = "DPN STAFF NON EMI";
},
{
"account_no" = 1000000552;
"account_type" = "DPN STAFF EMI LOANS";
})
I need to display the above data in UITableView like
section 0 --- Savings
Row 1 - 123
Row 2 - 123456
section 1 ---> DPN STAFF NON EMI
Row 1 - 00000316
Thanks,
AKC
You can make use of NSDictionary also. The below code worked perfectly.
if([arrySelectedDetails count] >0){
grouped = [[NSMutableDictionary alloc] initWithCapacity:arrySelectedAcctDetails.count];
for (NSDictionary *dict in arrySelectedDetails) {
id key = [dict valueForKey:#"type"];
NSMutableArray *tmp = [grouped objectForKey:key];
if (tmp == nil) {
tmp = [[NSMutableArray alloc] init];
[grouped setObject:tmp forKey:key];
}
[tmp addObject:dict];
}
typeArray= [[NSMutableArray alloc]init];
for(NSDictionary *groupId in arrySelectedDetails){
if(!([typeArray count]>0)){
[typeArray addObject:[groupId valueForKey:#"type"]];
}
else if (![typeArray containsObject:[groupId valueForKey:#"type"]]) {
[typeArray addObject:[groupId valueForKey:#"type"]];
}
}
}
Then for UITableView Delegates:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [typeArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [grouped[[typeArray objectAtIndex:section]] count]
}
Try the below code:
NSMutableArray *resultArray = [NSMutableArray new];
NSArray *groups = [arrySelectedAcctDetails valueForKeyPath:#"#distinctUnionOfObjects.account_type"];
NSLog(#"%#", groups);
for (NSString *groupId in groups)
{
NSMutableDictionary *entry = [NSMutableDictionary new];
[entry setObject:groupId forKey:#"account_type"];
NSArray *groupNames = [arrySelectedAcctDetails filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"account_type = %#", groupId]];
for (int i = 0; i < groupNames.count; i++)
{
NSString *name = [[groupNames objectAtIndex:i] objectForKey:#"account_no"];
[entry setObject:name forKey:[NSString stringWithFormat:#"account_no%d", i + 1]];
}
[resultArray addObject:entry];
}
NSLog(#"%#", resultArray);
Output:
{
"account_no1" = 00000316;
"account_type" = "DPN STAFF NON EMI";
},
{
"account_no1" = 123;
"account_no2" = 123456;
"account_type" = Savings;
},
Here's the source code for the method that appears to be causing the leak.
- (void)search:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
NSArray* fields = [command argumentAtIndex:0];
NSDictionary* findOptions = [command argumentAtIndex:1 withDefault:[NSNull null]];
[self.commandDelegate runInBackground:^{
// from Apple: Important You must ensure that an instance of ABAddressBookRef is used by only one thread.
// which is why address book is created within the dispatch queue.
// more details here: http: //blog.byadrian.net/2012/05/05/ios-addressbook-framework-and-gcd/
CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
// it gets uglier, block within block.....
[abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
if (addrBook == NULL) {
// permission was denied or other error - return error
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? (int)errCode.errorCode:UNKNOWN_ERROR];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
return;
}
NSArray* foundRecords = nil;
// get the findOptions values
BOOL multiple = NO; // default is false
NSString* filter = nil;
NSArray* desiredFields = nil;
if (![findOptions isKindOfClass:[NSNull class]]) {
id value = nil;
filter = (NSString*)[findOptions objectForKey:#"filter"];
value = [findOptions objectForKey:#"multiple"];
if ([value isKindOfClass:[NSNumber class]]) {
// multiple is a boolean that will come through as an NSNumber
multiple = [(NSNumber*)value boolValue];
// NSLog(#"multiple is: %d", multiple);
}
desiredFields = [findOptions objectForKey:#"desiredFields"];
// return all fields if desired fields are not explicitly defined
if (desiredFields == nil || desiredFields.count == 0) {
desiredFields = [NSArray arrayWithObjects:#"*", nil];
}
}
NSDictionary* searchFields = [[CDVContact class] calcReturnFields:fields];
NSDictionary* returnFields = [[CDVContact class] calcReturnFields:desiredFields];
NSMutableArray* matches = nil;
if (!filter || [filter isEqualToString:#""]) {
// get all records
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
if (foundRecords && ([foundRecords count] > 0)) {
// create Contacts and put into matches array
// doesn't make sense to ask for all records when multiple == NO but better check
int xferCount = multiple == YES ? (int)[foundRecords count] : 1;
matches = [NSMutableArray arrayWithCapacity:xferCount];
for (int k = 0; k < xferCount; k++) {
CDVContact* xferContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:k]];
[matches addObject:xferContact];
xferContact = nil;
}
}
} else {
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
matches = [NSMutableArray arrayWithCapacity:1];
BOOL bFound = NO;
int testCount = (int)[foundRecords count];
for (int j = 0; j < testCount; j++) {
CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]];
if (testContact) {
bFound = [testContact foundValue:filter inFields:searchFields];
if (bFound) {
[matches addObject:testContact];
}
testContact = nil;
}
}
}
NSMutableArray* returnContacts = [NSMutableArray arrayWithCapacity:1];
if ((matches != nil) && ([matches count] > 0)) {
// convert to JS Contacts format and return in callback
// - returnFields determines what properties to return
#autoreleasepool {
int count = multiple == YES ? (int)[matches count] : 1;
for (int i = 0; i < count; i++) {
CDVContact* newContact = [matches objectAtIndex:i];
NSDictionary* aContact = [newContact toDictionary:returnFields];
[returnContacts addObject:aContact];
}
}
}
// return found contacts (array is empty if no contacts found)
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:returnContacts];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
// NSLog(#"findCallback string: %#", jsString);
if (addrBook) {
CFRelease(addrBook);
}
}];
}]; // end of workQueue block
return;
}
The specific line that is doing most of the leaking is foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);, but this is confusing, given that the correct __bridge_transfer call is used. What's going on here?
I am using SQLite and I want to save the name, address, and phone text fields for them to show up in the next view controller for when the "show details" button is clicked in 1st VC.
I placed "save" and "show details" button in 1st VC, as well as "previous" and "next" button in 2nd VC. Whenever I click on "show details" I am getting this error message:
index 0 beyond bounds for empty array.
However, I see that the array is not empty. I want to store the student details in the array.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *homeDirectory = NSHomeDirectory();
NSString *documentsDirectoryPath = [homeDirectory stringByAppendingPathComponent:#"Documents"];
self.dbFilePathInDocuments = [documentsDirectoryPath stringByAppendingPathComponent:#"details.db"];
self.studentDetails = [[NSMutableArray alloc]init];
NSString *selectQuery = [NSString stringWithFormat:#"select name,address,phone from contacts"];
sqlite3_open([self.dbFilePathInDocuments UTF8String], &dataBase);
sqlite3_prepare_v2(dataBase, [selectQuery UTF8String], -1,&selectStatement, NULL);
while (sqlite3_step(selectStatement) == SQLITE_ROW)
{
NSMutableDictionary *studentDict = [[NSMutableDictionary alloc]init];
NSString *name = [NSString stringWithFormat:#"%s",sqlite3_column_text(selectStatement, 0)];
NSString *address = [NSString stringWithFormat:#"%s",sqlite3_column_text(selectStatement, 1)];
NSString *phone = [NSString stringWithFormat:#"%s",sqlite3_column_text(selectStatement, 2)];
[studentDict setObject:name forKey:#"name"];
[studentDict setObject:address forKey:#"address"];
[studentDict setObject:phone forKey:#"phone"];
[self.studentDetails addObject:studentDict];
NSLog(#"student is:%#",self.studentDetails);
}
sqlite3_finalize(selectStatement);
sqlite3_close(dataBase);
self.nameLabel.text = [[self.studentDetails objectAtIndex:0] valueForKey:#"name"];
self.addressLabel.text = [[self.studentDetails objectAtIndex:0] valueForKey:#"address"];
self.phoneLabel.text = [[self.studentDetails objectAtIndex:0] valueForKey:#"phone"];
currentStudentIndex = 0;
}
- (IBAction)clickPrevious:(id)sender {
if(currentStudentIndex <=0)
{
currentStudentIndex = 0;
}else
{
currentStudentIndex = currentStudentIndex - 1;
}
self.nameLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"name"];
self.addressLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"address"];
self.phoneLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"phone"];
}
- (IBAction)clickNext:(id)sender {
if(currentStudentIndex >= [self.studentDetails count] - 1)
{
currentStudentIndex = [self.studentDetails count] - 1;
}else
{
currentStudentIndex = currentStudentIndex + 1;
}
self.nameLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"name"];
self.addressLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"address"];
self.phoneLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"phone"];
}
The issue is that you always accessing the array self.studentDetails even if it's empty. This will cause an exception.
First limit setting of the labels to a single method and check the array access will succeed before attempting it:
- (void)updateLabels
{
if (currentStudentIndex >= [self.studentDetails count])
return;
self.nameLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"name"];
self.addressLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"address"];
self.phoneLabel.text = [[self.studentDetails objectAtIndex:currentStudentIndex] valueForKey:#"phone"];
}
and use that method in the 3 places you currently set the labels. For example:
- (IBAction)clickPrevious:(id)sender {
currentStudentIndex--;
[self updateLabels];
}
- (IBAction)clickNext:(id)sender {
currentStudentIndex++;
[self updateLabels];
}
In the viewDidLoad method use this code:
...
sqlite3_finalize(selectStatement);
sqlite3_close(dataBase);
currentStudentIndex = 0;
[self updateLabels];
After that you're gonna want to work on enabling/disabling buttons depending on whether there is a next or previous student to view to make using the app more intuitive.
I am using REST API to fetch data. I have two methods, one for ResponceSucces and other for responceFailure. In responce failure I simply dismiss the activity indicator. Sometimes it happens that data is not retrieved but still activity indicator keeps spinning infinitely. And ResponceFailure method is not Called.
My question is can I set timeout interval to my web service, to check if response did not come, then activity indicator must be dismissed after some time for example 20/30 seconds.
Below is my code:
-(void)messagesGetSuccess:(FBListingsWebHandler*)handler response:(NSDictionary*)response
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[FBModelManager sharedModelManager].messages removeAllObjects];
NSArray *messagesArray = [response objectForKey:#"message"];
if([[FBUserManager sharedUserManager] userType] == kUserShipper){
//extract usernames of transporters who placed bid on selected listing
NSArray *userNames;
NSMutableArray *transporterNames;
NSDictionary *biddingsDic = [[FBModelManager sharedModelManager] getModelDictionary:kModelBiddings];
if ([biddingsDic count] > 0){
userNames = [[biddingsDic allValues] valueForKey:#"userName"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:userNames];
NSSet *uniqueNames = [orderedSet set];
transporterNames = [[NSMutableArray alloc] initWithArray:[uniqueNames allObjects]];
}
//add transporter records in messages dictionary who have messages for selected listing
for (int i = 1; i < [messagesArray count]; i++) {
NSMutableArray *modelArray = [[NSMutableArray alloc] init];
NSDictionary *groupedMsgDic = [messagesArray objectAtIndex:i];
int count = (int)[[[groupedMsgDic allValues] objectAtIndex:0] count];
for (int j = 0; j < count; j++) {
FBMessagesModel *model = [[FBMessagesModel alloc] initWithMessageItems:[[[groupedMsgDic allValues] objectAtIndex:0] objectAtIndex:j]];
[modelArray addObject:model];
model = nil;
}
[[FBModelManager sharedModelManager].messages setObject:modelArray forKey:[[groupedMsgDic allKeys] objectAtIndex:0]];
//remove transporter usernames from array who have messages besides bid for selected listing
if([transporterNames containsObject:[[groupedMsgDic allKeys] objectAtIndex:0]]){
[transporterNames removeObject:[[groupedMsgDic allKeys] objectAtIndex:0]];
}
modelArray = nil;
}
//add transporter records in messages dictionary who placed bid but have no message for selected listing
for (int i = 0; i < [transporterNames count]; i++) {
NSString *path = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] photoPath];
int transporterId = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] userId];
NSString *objectForZeroMsgs = [NSString stringWithFormat:#"%#-%d",path,transporterId];
[[FBModelManager sharedModelManager].messages setObject:objectForZeroMsgs forKey:[transporterNames objectAtIndex:i]];
}
transporterNames = nil;
// [self tableView:self.shipperGroupedMessagesTableView_ numberOfRowsInSection:0];
[self.shipperGroupedMessagesTableView_ reloadData];
}else if([[FBUserManager sharedUserManager] userType] == kUserTransporter){
for (int i = 1; i < [messagesArray count]; i++) {
FBMessagesModel *model = [[FBMessagesModel alloc] initWithMessageItems:[messagesArray objectAtIndex:i]];
[[FBModelManager sharedModelManager].messages setObject:model forKey:[NSString stringWithFormat:#"%d",model.mailId]];
if(model.mailMessage && ![model.mailMessage isEqualToString:#""]){
[self createMessageBubble:model];
}
model = nil;
}
[self.bubbleTableView_ reloadData];
}
}
-(void)messagesGetFailure:(FBListingsWebHandler*)handler
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[FBModelManager sharedModelManager].messages removeAllObjects];
if([[FBUserManager sharedUserManager] userType] == kUserTransporter){
[self.bubbleTableView_ reloadData];
}
else if([[FBUserManager sharedUserManager] userType] == kUserShipper){
if([[FBModelManager sharedModelManager].messages count] == 0){
NSDictionary *biddingsDic = [[FBModelManager sharedModelManager] getModelDictionary:kModelBiddings];
if ([biddingsDic count] > 0){
NSArray *userNames = [[biddingsDic allValues] valueForKey:#"userName"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:userNames];
NSSet *uniqueNames = [orderedSet set];
NSArray *transporterNames = [uniqueNames allObjects];
[[FBModelManager sharedModelManager].messages removeAllObjects];
for (int i = 0; i < [transporterNames count]; i++) {
NSString *path = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] photoPath];
int transporterId = [[[biddingsDic allValues] objectAtIndex:[userNames indexOfObject:[transporterNames objectAtIndex:i]]] userId];
NSString *objectForZeroMsgs = [NSString stringWithFormat:#"%#-%d",path,transporterId];
[[FBModelManager sharedModelManager].messages setObject:objectForZeroMsgs forKey:[transporterNames objectAtIndex:i]];
}
[self.shipperGroupedMessagesTableView_ reloadData];
}else{
[self addNoMessagesLabel];
}
}
[self.shipperGroupedMessagesTableView_ reloadData];
}
}
I am adding a screenshot.
It's driving me crazy, I did a log and I see the objects are different, but when I get then back from NSUserDefaults, all of the objects are the same.
My code:
- (void)breakTrapsToSave:(NSDictionary*)trapsDict firstTimeUpdate:(Boolean)firstTimeUpdate
{
// If traps already save
// we will get them from NSUserDefaults
// and then update them
if (!firstTimeUpdate)
{
allTraps = [self.sharedPrefs objectForKey:#"arrayOfAllTraps"];
}
// JSON Parsing
tempA = trapsDict[#"Envelope"];
tempB = tempA[#"Body"];
tempC = tempB[#"getTrapsResponse"];
tempD = tempC[#"getTrapsResult"];
tempE = tempD[#"TRAPS"];
self.lastUpdate = tempE[#"lastUpdate"];
[[NSUserDefaults standardUserDefaults] setObject:self.lastUpdate forKey:#"last_update"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"Traps latest updated at: %#", self.lastUpdate);
tempF = tempE[#"TRAP"];
if (tempF.count <= 0)
{
newTrapsUpdates = false;
NSLog(#"NO NEW TRAPS!");
}
else
{
newTrapsUpdates = true;
NSLog(#"NEW TRAPS FOUND");
[tempF enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
tempA = tempF[idx];
NSString *finalResult;
NSString *key;
NSMutableDictionary *singleTrap = [[NSMutableDictionary alloc] init];
for (int i=0; i < node.count; i++)
{
finalResult = tempA[node[i]];
key = node[i];
if ([finalResult length] <= 0)
{
finalResult = #"0";
}
singleTrap[key] = finalResult;
}
if (allTraps.count <= 0)
{
allTraps = [[NSMutableArray alloc] initWithObjects:singleTrap, nil];
}
else
{
[allTraps addObject:singleTrap];
}
counter = idx;
}];
allTraps = [[IDANNetroads sharedInstance] removeDuplicatedFromArray:allTraps];
// Save all traps
[[NSUserDefaults standardUserDefaults] setObject:allTraps forKey:#"arrayOfAllTraps"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"Total Traps: %d", allTraps.count);
NSLog(#"Total New Traps: %d", counter);
}
}
I did a log and I see allTraps[idx] = singleTrap; is different as it should be, but when I print the log for NSLog(#"allTraps: %#", allTraps); I see all of the objects are the last object.
EDIT:
Eventually, I replaced the singleTrap allocation and now it's inside the enumeration block:
NSMutableDictionary *singleTrap = [[NSMutableDictionary alloc] init];
And I added this code:
if (allTraps.count <= 0)
{
allTraps = [[NSMutableArray alloc] initWithObjects:singleTrap, nil];
}
else
{
[allTraps addObject:singleTrap];
}
So, the final code is edited.
Whatever singleTrap is, you're repeatedly mutating it and storing another reference to the same object in your allTraps array. You need to create (instantiate) a new item for each entry you want in your allTraps list.
It looks like singleTrap is an array of strings, so try:
allTraps[idx] = [singleTrap copy];