iPhone: Get value in multi-dimentional array - iphone

I have an array that is many levels deep and I am wondering what is the best way to get one of the child element values that is deep in my array.
I assume I need to use a recursive method, but what is the best way to do this? Or is there a faster way to do this?
The array comes from an XML Parser that I am using which builds everything into an array like this (using NSLog to show the structure):
{
children = (
{
children = (
{
children = (
{
children = (
);
data = 12;
element = AssetID;
}
);
data = "";
element = "ns1:GetUserIdByUsernameResponse";
}
);
data = "";
element = "SOAP-ENV:Body";
}
);
data = "";
element = "SOAP-ENV:Envelope";
}
What I would like to get at is the AssetID data, which in this case is 12.

Looks like the parser is returning an NSDictionary for each element with a "children" key that is an NSArray of child elements.
If the response will always be in this format with the AssetID element always in this position, you can directly access it without using recursion or looping.
Use something like this:
//"parserResultObject is the object the parser returns to you
NSDictionary *bodyDict = [[parserResultObject objectForKey:#"children"] objectAtIndex:0];
NSDictionary *responseDict = [[bodyDict objectForKey:#"children"] objectAtIndex:0];
NSDictionary *assetIdDict = [[responseDict objectForKey:#"children"] objectAtIndex:0];
//assuming AssetID's data is stored as an NSNumber...
int assetIdData = [[assetIdDict objectForKey:#"data"] intValue];

Related

How to dereference entries in a hash of arrays of hashes

I have a data structure that was built like so:-
$ICVDWKey = "($LLX $LLY) ($URX $URY)";
...
push #{$ICVDWStats{$ICVDWKey}}, {
ICVDensity=>$Density,
ICVLayerArea=>$LayerArea,
ICVWindowArea=>$WindowArea
};
I can dereference its contents like so...
foreach $ICVDWKey (#AllICVDWCoords) {
foreach (#{$ICVDWStats{$ICVDWKey}}) {
$ICVDensity = $_->{ICVDensity};
$ICVLayerArea = $_->{ICVLayerArea};
$ICVWindowArea = $_->{ICVWindowArea};
...
}
}
...and everything is good. However, I am running into problems when another data structure is built the same way and I need to check its contents when looping through the original data structure, above. Here is an example...
foreach $ICVDWKey (#AllICVDWCoords) {
foreach (#{$ICVDWStats{$ICVDWKey}}) {
$ICVDensity = $_->{ICVDensity};
$ICVLayerArea = $_->{ICVLayerArea};
$ICVWindowArea = $_->{ICVWindowArea};
...
if (exists ($ICC2DWStats{$ICVDWKey})) {
$ICC2Density = $_->{ICC2Density};
$ICC2LayerArea = $_->{ICC2LayerArea};
$ICC2WindowArea = $_->{ICC2WindowArea};
...
}
}
}
I know the if exists $ICVDWKey matching is working properly, but I cannot cleanly dereference the contents of the ICC2DWStats hash data. What is the proper what to retrieve the ICC2* data when $ICVDWKey keys match between the two data structures? I am sure it is the $_ in the ICC2* references, but I do not know what should be used instead.
Thanks!
Instead of using $_ which represents a structure other than the $ICC2DWStats hashref which you want, you need to explicitly specify the hash and key of the actual hasn you want to extract from:
for $ICVDWKey (#AllICVDWCoords) {
for (#{$ICVDWStats{$ICVDWKey}}) {
$ICVDensity = $_->{ICVDensity};
$ICVLayerArea = $_->{ICVLayerArea};
$ICVWindowArea = $_->{ICVWindowArea};
...
if (exists ($ICC2DWStats{$ICVDWKey})) {
$ICC2Density = $ICC2DWStats->{$ICVDWKey}{ICC2Density};
$ICC2LayerArea = $ICC2DWStats->{$ICVDWKey}{ICC2LayerArea};
$ICC2WindowArea = $ICC2DWStats->{$ICVDWKey}{ICC2WindowArea};
...
}
}
}
Note that you should be using use strict; and use warnings;.

Array always showing the value of index in all the index values in iphone application

I have array in which i insert all the values in sqlite which are different and are stored in the table but when i access the array it shows the same values for all the array indexex i have four items in the array
This how i am adding the items
for (int i = 0; i<[surveyQuestions count]; i++) {
QuestionData*data=[surveyQuestions objectAtIndex:i];
question_text=data.question_text;
question_type=data.question_type;
coffeeObj.question_text =question_text;
coffeeObj.question_type=question_type;
NSLog(#"Inserted question %#",question_text);
[appDelegate addCoffee:coffeeObj];
}
- (void) addCoffee:(QuestionData *)coffeeObj {
[coffeeObj addCoffee];
[coffeeArray addObject:coffeeObj];
NSLog(#"Succeessfully Added");
}
for(int j=0;j<countmine;j++)
{
QuestionData*data=[appDelegate.coffeeArray objectAtIndex:j];
NSString*myTest=data.question_text;
NSLog(myTest);
QuestionData*dataset=[appDelegate.coffeeArray objectAtIndex:0];
questionTextLabel.text=dataset.question_text;
}
In data base i am adding the array but it always showing the last enterend values in each index in the data base
You have used index 0 while assigning the value to questionTextLabel. You should have use "j" here.
//In your answer its 0 here
QuestionData*dataset=[appDelegate.coffeeArray objectAtIndex:j];
questionTextLabel.text=dataset.question_text;

How can I get a list of all the properties of an ABRecordRef?

I would like to obtain a list of all the properties of an ABPersonRef and ABGroupRef without having to use the iOS predefined keys of kABPersonFirstNameProperty, kABPersonLastNameProperty... I'm playing with the address book and I'd like to iterate over all values for a particular person. I know there are predefined keys but Apple could very well add new ones in the future so I'd like to do something like:
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *allPeople = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (int i = 0; i < [allPeople count]; ++i) {
ABRecordRef person = [allPeople objectAtIndex:i];
// This is the line that I can't figure out.
NSArray *allProperties = (NSArray *)ABRecordCopyArrayOfAllProperties(person);
}
I know that I'll encounter multivalue items that I'll have to loop though later, but the goal is to obtain a list of keys that I can iterate over for the single value properties. I don't care what the returned class is, NSArray, NSDictionary... whatever.
I greatly appreciate any advice!
You can try the following:
With ARC:
NSDictionary* dictionaryRepresentationForABPerson(ABRecordRef person)
{
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
for ( int32_t propertyIndex = kABPersonFirstNameProperty; propertyIndex <= kABPersonSocialProfileProperty; propertyIndex ++ )
{
NSString* propertyName = CFBridgingRelease(ABPersonCopyLocalizedPropertyName(propertyIndex));
id value = CFBridgingRelease(ABRecordCopyValue(person, propertyIndex));
if ( value )
[dictionary setObject:value forKey:propertyName];
}
return dictionary;
}
We using the localized name of the property - in different locales will have different keys.
The number of properties may change in the next version of iOS.
Maybe it makes sense to go through to the number of properties as long as the propertyName does not become UNKNOWN_PROPERTY
Aliaksandr's solution is not safe: for example if you attempt to create ABPerson records in a specific ABSource, and use this approach, you might find that contacts do not sync to that source properly.
I simply copied the list of 25 ABPropertyIDs from ABPerson, stuck them in a simple int[], and iterated over them...
// Loop over all properties of this Person
// taken from Apple's ABPerson reference page on 9.12.13.
// URL: https://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABPersonRef_iPhoneOS/Reference/reference.html#//apple_ref/c/func/ABPersonGetTypeOfProperty
// count = 25. All are type ABPropertyID
int propertyArray[25] = {
kABPersonFirstNameProperty,
kABPersonLastNameProperty,
kABPersonMiddleNameProperty,
kABPersonPrefixProperty,
kABPersonSuffixProperty,
kABPersonNicknameProperty,
kABPersonFirstNamePhoneticProperty,
kABPersonLastNamePhoneticProperty,
kABPersonMiddleNamePhoneticProperty,
kABPersonOrganizationProperty,
kABPersonJobTitleProperty,
kABPersonDepartmentProperty,
kABPersonEmailProperty,
kABPersonBirthdayProperty,
kABPersonNoteProperty,
kABPersonCreationDateProperty,
kABPersonModificationDateProperty,
kABPersonAddressProperty,
kABPersonDateProperty,
kABPersonKindProperty,
kABPersonPhoneProperty,
kABPersonInstantMessageProperty,
kABPersonSocialProfileProperty,
kABPersonURLProperty,
kABPersonRelatedNamesProperty
};
int propertyArraySize = 25;
for ( int propertyIndex = 0; propertyIndex < propertyArraySize; propertyIndex++ ) {
...code here
}

iphone - Make a tree class with cftree

I want to make a tree class like NSArray with CFTree.
(Actually NSTree doesn't exist so I'm trying to make 'NSTree like tree'.)
But CFTree seems to request certain type class.
I want to make a general class that can deal in various class like NSArray.
Here is iPhone dev site's example.
static CFTreeRef CreateMyTree(CFAllocatorRef allocator) {
MyTreeInfo *info;
CFTreeContext ctx;
info = CFAllocatorAllocate(allocator, sizeof(MyTreeInfo), 0);
info->address = 0;
info->symbol = NULL;
info->countCurrent = 0;
info->countTotal = 0;
ctx.version = 0;
ctx.info = info;
ctx.retain = AllocTreeInfo;
ctx.release = FreeTreeInfo;
ctx.copyDescription = NULL;
return CFTreeCreate(allocator, &ctx);
}
I want to use general class instead of "MyTreeInfo".
Is there any way?
Thanks.
Sure, CFTree can hold any data you want. If you want to store instances of CFType, then just set the retain and release of the context to CFRetain and CFRelease. You can also then set copyDescription to CFCopyDescription. Something like this:
static CFTreeRef CreateMyTree(CFTypeRef rootObject) {
CFTreeContext ctx;
ctx.version = 0;
ctx.info = rootObject;
ctx.retain = CFRetain;
ctx.release = CFRelease;
ctx.copyDescription = CFCopyDescription;
return CFTreeCreate(NULL, &ctx);
}
...
CFStringRef string = CFSTR("foo");
CFTreeRef tree = CreateMyTree(string);
NSLog(#"%#", tree);
CFRelease(tree);

Where can I find updated, live exchange rates?

How do I link live currency exchange rates to my iPhone app? First, anyone know any sites where I can get the exchange rates? And second, how do I link that to my app? I want to do what this app does. http://the-dream.co.uk/currencee/
Here is a blog post about this, however to recap, if you use TBXML you can do it with the methods below.
They do the following:
Assume you have made a mutable dictionary object as a class property called exchangeRates
Set's EUR as the base rate (value of 1.0)
Call the European Central Bank's exchange rate XML feed and parses it.
After you've called the loadExchangeRates() method you can obtain a specific exchange rate by doing:
NSDecimalNumber *rate = [NSDecimalNumber decimalNumberWithString:[self.exchangeRates objectForKey:#"USD"]];
Here are the methods:
- (void)loadExchangeRates {
// initialize rate array
exchangeRates = [[NSMutableDictionary alloc] init];
// Load and parse the rates.xml file
TBXML * tbxml = [[TBXML tbxmlWithURL:[NSURL URLWithString:#"http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml"]] retain];
// If TBXML found a root node, process element and iterate all children
if (tbxml.rootXMLElement)
[self traverseElement:tbxml.rootXMLElement];
// add EUR to rate table
[exchangeRates setObject:#"1.0" forKey:#"EUR"];
// release resources
[tbxml release]; }
- (void) traverseElement:(TBXMLElement *)element {
do {
// Display the name of the element
//NSLog(#"%#",[TBXML elementName:element]);
// Obtain first attribute from element
TBXMLAttribute * attribute = element->firstAttribute;
// if attribute is valid
NSString *currencyName;
while (attribute) {
/* Display name and value of attribute to the log window
NSLog(#"%#->%# = %#",
[TBXML elementName:element],
[TBXML attributeName:attribute],
[TBXML attributeValue:attribute]);
*/
// store currency
if ([[TBXML attributeName:attribute] isEqualToString: #"currency"]) {
currencyName = [TBXML attributeValue:attribute];
}else if ([[TBXML attributeName:attribute] isEqualToString: #"rate"]) {
// store currency and rate in dictionary
[exchangeRates setObject:[TBXML attributeValue:attribute] forKey:currencyName];
}
// Obtain the next attribute
attribute = attribute->next;
}
// if the element has child elements, process them
if (element->firstChild)
[self traverseElement:element->firstChild];
// Obtain next sibling element
} while ((element = element->nextSibling));
}
I realise this question has been answered already, but for anyone else looking for a solution to this same issue, there's also a great JSON solution available at openexchangerates.org as well.
My first port of call would be to find a webservice that provides currency rates with a public API.
Then you'd need to integrate some functionality into your app that communicates with the API in order to get the information you need.
There might be some services that offer the exchange rates in an RSS feed or similar feed. You could then parse the XML downloaded from that feed into some objects that you can use in your app.
Average monthly exchange rates of GBP to other currencies you can find as xml in link -
string url = "http://www.hmrc.gov.uk/softwaredevelopers/rates/exrates-monthly-" +
month2SymbolsYear2SymbolsString + ".xml";
then you can load this xml into lists and use in your code -
string xmlStr;
using (var wc = new WebClient())
{
xmlStr = wc.DownloadString(url);
}
var doc = XDocument.Parse(xmlStr);
var currenciesCodes = doc.Root.Elements().Select(x => x.Element("currencyCode"));
var rates = doc.Root.Elements().Select(x => x.Element("rateNew"));
List<string> currenciesCodesList = new List<string>();
foreach (var code in currenciesCodes)
{
currenciesCodesList.Add(code.Value);
}
List<double> currenciesRatesToGBPList = new List<double>();
foreach (var rate in rates)
{
double rateDouble;
if (!Double.TryParse(rate.Value, out rateDouble))
{
errorMessage = "During monthly average exchanges rates loading from page" + "\r\n" +
url + "\r\n" +
"program found text value - " + rate.Value + "\r\n" +
"which can't be converted to double value" + "\r\n" +
"Program can't correctly form reports and will end now.";
return errorMessage;
}
currenciesRatesToGBPList.Add(rateDouble);
}