How would I filter through an array and return values that contain a certain part of a string? I have a text box where, for the sake of this example, a user puts in 25, and then hits a "Done" button.
Example:
Original Array {25-1002, 25-1005, 12-1003, 1000-0942, 1-1, 234-25}
I want it to return (after sorting through it and pulling the values I want):
New Array {25-1002, 25-1005}
Please note that in the original array, the last value of 234-25 has a 25 in it as well but is not pulled through. It needs to be the number on the first part of the hyphen.
Thanks in advance!
Use the -filteredArrayUsingPredicate: method, like this:
NSString *searchText = [someField.text stringByAppendingString:#"-"];
newArray = [originalArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^(NSString *value, NSDictionary *bindings){
return ([value rangeOfString:searchText].location != NSNotFound)
}]];
Note that blocks (the ^{} thing) aren’t available pre-iOS 4, so you’ll have to use another of NSPredicate’s constructors if you’re targeting 3.x devices as well.
as an easy to understand answer (not using NSPredicate, which can be intimidating (but is really the correct way to do it)):
NSMutableArray *myNewArray = [[NSMutableArray alloc] init];
for (NSString *string in myArray) {
if([[string substringToIndex:3] isEqualToString #"25-"])
{
[myNewArray addObject:string];
}
}
Related
Currently I'm programming an app with a tableView, similar to that one in the iPhone Contacts app.
Everything works (the sections, the bar on the right showing the titles, the cells are configured...), beside the search bar. I'm familiar how to do this (search) if the tableView's data is loaded from an array, but my situation is that its loaded from arrays located in a NSDictionary.
The dict looks like
Key = "A" >> Value = "array = apple, animal, alphabet, abc ..."
Key = "B" >> Value = "array = bat, ball, banana ..."
How can I remove all strings (from all of the dictionary's arrays) matching the search term?
Thanks a lot in advance :)
Well you can do it like this
NSMutableDictionary *newItems = [NSMutableDictionary dictionary];
for (NSString *key in oldItems) {
NSMutableArray *newArray = [NSMutableArray array];
for (NSString *item in [oldItems objectForKey:key]) {
if ([item rangeOfString:searchTerm].location != NSNotFound) {
[newArray addObject:item];
}
}
if ([newArray count]) {
[newItems setObject:newArray forKey:key];
}
}
[oldItems release];
oldItems = [newItems retain];
I don't know if this is the best way to do it or even if it's faster enough but let me know if this works for you.
Did you want to update the existing Dictionary with the new Array that excludes that string?
NSMutableDictionary* excludedDictionary = [NSMutableDictionary dictionaryWithDictionary:existingDictionary];
for(id key in [existingDictionary allKeys])
{
NSArray* existingArray = [existingDictionary objectForKey:key];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self != %#", excludedString];
NSArray* excludedArray = [existingArray filteredArrayUsingPredicate:predicate];
[excludedDictionary setObject:excludedArray forKey:key];
}
existingDictionary = [NSDictionary dictionaryWithDictionary:excludedDictionary];
This will replace your existing dictionary with one that doesn't have the string in it...
From you comments, I understand that you want to filter the table contents on the basis of what the user enters in the text field. For this, you do not need to modify your dictionary at every character change. The UISearchDisplayController is provided for exactly this scenario. Have a look at the reference for details: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UISearchDisplayController_Class/Reference/Reference.html.
HTH,
Akshay
I'm writing a custom xml deserializer for an iphone app. As you can see below, I'm looping through all the list elements in the xml, I have debugged it, and with each loop there is a new and different element. The problem is that the xpath helper methods (there are similar ones to the one posted below, but for int and decimal) always returns the same value.
For example - 1st loop's xml "SomeValue" value will be "abc" and the helper method will return "abc", second item comes around and its xml "SomeValue" is "XYZ", but the helper method will still return "abc".
I'm new to iphone/objective c/memory managment so it could be any number of things. I just cant determine what the problem is :( could someone please offer some help?
-(void) deserializeAndCallback:(GDataXMLElement *)response
{
NSError * error;
NSArray *listings = [response nodesForXPath:#"//ListingSummary" error:&error];
NSMutableArray *deserializedListings = [[NSMutableArray alloc] init];
//loop through all listing elements, create a new listing object, set its values, and add
//it to the list of deserialized objects.
if(listings.count > 0)
{
for (GDataXMLElement *listingElement in listings)
{
Listing *list = [[Listing alloc] init];
//xpath helper function (shown below), just to get the value out of the xml
list.someValue = [QuickQuery getString:listingElement fromXPath:#"//SomeValue"];
[deserializedListings addObject:list];
}
}
if([super.delegate respondsToSelector: #selector(dataReady:)])
{
[super.delegate dataReady:deserializedListings];
}
}
+(NSString *) getString:(GDataXMLElement *)element fromXPath:(NSString *)xPath
{
NSError *error;
NSArray *result = [element nodesForXPath:xPath error:&error];
if(result.count > 0)
{
GDataXMLElement *singleValue = (GDataXMLElement *) [result objectAtIndex:0];
return singleValue.stringValue;
[singleValue release];
}
return nil;
[error release];
[result release];
}
EDIT: ok... I found a bit more info. Inside the helper function, the nodesForXpath method returns all the nodes from the entire xml, not just the current element I'm busy with. Does GDataXMLElement keep reference to its parent elements at all?
Example of what the xml looks like
<ListingSummary>
<SomeValue>abc</SomeValue>
</ListingSummary>
<ListingSummary>
<SomeValue>jhi</SomeValue>
</ListingSummary>
<ListingSummary>
<SomeValue>xyz</SomeValue>
</ListingSummary>
What you are seeing is correct behaviour for the XPath query you are using. You actually want a query relative to the current node, not the root of the document as you are doing.
See http://www.w3schools.com/xpath/
BTW + (NSString *)getString:(GDataXMLElement *)element fromXPath:(NSString *)xPath is a class method, not a static method.
You say that nodesForXPath: returns all the nodes from the whole document. Since you are calling that method with the same argument, #"//SomeValue", every loop, you get back the same array every time. This means that [result objectAtIndex:0] gives you the same object every time.
Also, as I mentioned in a comment, you should not be releasing singleValue, error, or result in your helper method. You don't own those and you're not responsible for their memory. On the other hand, since you create list using alloc, you do need to release it at the end of each loop; you are currently leaking a Listing object every pass.
It looks OK for me. Although the releases inside the getString:fromXPath: aren't necessary (you don't need to release parameters entered into a method or objects obtained from a NSArray. The proper way to release an object from a NSArray is removing it from the array, as for objects passed as a parameter, if you want to release it you should do it after you call the method.
The problem to your question must be somewhere else.
result.count should be [result count] since count is a method and not a property of NSArray
i wanted to know how to add strings into an array.
I used the following methods but it is showing null.
1) [arrData addObject:[NSString stringWithString:strlast]];
2) [arrData addObject:strlast];
Thanks in advance
You can't add anything to an NSArray once it's created. You need to use an NSMutableArray if you want to make changes to it.
Update: You may actually have two problems.
Using an NSArray instead of an NSMutableArray when mutability is needed.
Not initializing the array object (either kind). If arrData is nil, you can happily send as many messages as you want to nil. Nothing will happen.
If it is showing null (nil) you need to make sure you set arrData somewhere in your code before trying to addObject:.
arrData = [[NSMutableArray alloc] init];
Also strlast is a string so use your second example, the first example is pointless.
[arrData addObject:strlast];
Did you allocate an array and assign it to arrData?
Try:
NSMutableArray *arrData = [NSMutableArray array];
NSString *string = #"My string";
[arrData addObject:string];
NSLog(#"%#", [arrData objectAtIndex:0]); //logs "My string"
If you're using a non-mutable array, you can also use arrayByAddingObject:
arrData = [arrData arrayByAddingObject: strlast];
but a mutable array is probably a better idea.
I’m making a languages application, and I have a long list of vocabulary relating to that language (German, in case anyone was interested). I have the functionality in my app to switch between sorting the tableview by German words, or by english words.
When I use the following:
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(compare:)];
it works absolutely perfectly - by which I mean, exactly as expected. What I would like to improve on this however, is that there are certain words, such as verbs or nouns, which are always preceded by prefixes, like “to”, as in “to do something”, or “the” in front of nouns. So what I would like to do is somehow exclude these from my sort, because otherwise I end up with all the verbs being sorted alphabetically under the “t” section in my array, which is not very user friendly.
I’ve looked through the Apple documentation about NSString and NSArray, as this is where the compare function is (unless I’m very much mistaken), and I haven’t found any way that makes sense to me. This is the first time I have done any data handling like this so I may be missing something simple, and so I would really appreciate some help.
Thanks very much
Michaeljvdw
You're on the right track. What you want to use instead of the (built-in) compare method is to write your own method, which can eliminate the "to" or "the" bits if they exist, and then use the existing compare method.
Your call would look something like this:
NSArray *array = [[string componentsSeparatedByString:#"\n"] sortedArrayUsingSelector:#selector(myCompare:)];
Using a custom category you give to NSString with the following methods:
// This method can be exposed in a header
- (NSComparisonResult)myCompare:(NSString*)aString
{
NSString* selfTrimmed = [self removeArticles];
NSString* aStringTrimmed = [s2 removeArticles];
return [self compare:aString];
}
// This method can be kept private in the .m implementation
- (NSString*)removeArticles
{
NSRange range = NSMakeRange(NSNotFound, 0);
if ([self hasPrefix:#"to "])
{
range = [self rangeOfString:#"to "];
}
else if ([self hasPrefix:#"the "])
{
range = [self rangeOfString:#"the "];
}
if (range.location != NSNotFound)
{
return [self substringFromIndex:range.length];
}
else
{
return self;
}
}
You might have some luck with localizedCompare: or localizedStandardCompare:, but I don't think that either of these will strip out articles and prepositions like you want. Instead, you will probably have to define a category on NSString that provides the specific style of sorting you're looking for:
#interface NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other;
#end
#implementation NSString (MySortAdditions)
- (NSComparisonResult)compareWithoutArticles:(NSString *)other {
NSMutableString *mutableSelf = [NSMutableString stringWithString:self];
[mutableSelf
replaceOccurrencesOfString:#"das"
withString:#""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [mutableSelf length])
];
...
// delete articles from 'other' too
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *trimmedSelf = [mutableSelf stringByTrimmingCharactersInSet:trimSet];
NSString *trimmedOther = ...;
return [trimmedSelf localizedCaseInsensitiveCompare:trimmedOther];
}
#end
You can then use #selector(compareWithoutArticles:) as your sort selector for NSArray.
First, don't use compare:. Use localizedCompare: instead. This is important, because whether á appears just after a or after z as a separate letter depends on the language. localizedCompare: takes care of that.
--edit
As Justin says, localizedStandardCompare: is the selector to be used! I didn't know that method. As written in the documentation, localizedStandardCompare: does more than localizedCompare:, although the document doesn't say exactly what it does.
--end of edit
If you want more, you need to implement that yourself. You can use category for that purpose. First declare it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string;
#end
and then implement it
#interface NSString (MichaelsSuperCompareCategory)
-(NSComparisonResult)michaelsSuperCompare:(NSString*)string{
...
}
#end
This way you can add methods to an existing class. Then you can use
NSArray *array = [[string componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector:#selector(michaelsSuperCompare:)];
It is important to prefix the method name with something distinctive, not to accidentally crash with internal methods used by Apple.
As for the functionality, you need to implement that yourself, as far as I know. You can get the current locale with [NSLocale currentLocale]. You can implement a nicer behavior for the languages you know, and then default to localizedCompare: for unknown languages.
I would somehow do -replaceOccurancesOfStrings on all the data eg "To" -> "" - and then reload the data. (or this can in a text editor)
Another thing to think about is having eg 'to walk' changed to 'walk (to)' which can be done ahead of time (and will also create less confusion for the user as they are scrolling alphabetically).
i have created NSMutableDictionary with 10 keys.Now i want to access NSMutableDictionary keys in a same order as it was added to NSMutableDictionary (using SetValue:* forKey:* );
How can i achieve that ?
If you absolutely must use a dictionary container, you have to use a key that is sortable by the order in which you add key-value pairs. Thus, when creating your dictionary, you use a key that is an auto-incrementing integer or similar. You can then sort on the (integer) keys and retrieve the values associated with those keys.
If you do all of that, however, you may as well just use an NSMutableArray and add values to the array directly! It will be much faster and require less code. You just retrieve objects in order:
for (id obj in myArray) { /* do stuff with obj... */ }
NSMutableDictionary can't do that. Take a look at e.g. Matt Gallaghers OrderedDictionary.
I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.
- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [referenceArray count]; i++)
{
if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
{
[returnArray addObject:[arrReference objectAtIndex:i]];
}
}
return [returnArray copy];
}
Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.
All that said, if you're trying to do something simple, it should work nicely. In your case, you could build up the reference array as you are looping through your setValue:forKey:.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (//whatever you are looping through here)
{
[yourDictionary setValue://whatever forKey:key];
[referenceArray addObject:key];
}
Then, when you want to loop over your items in the order they came in, you just
for (NSString *key in [self reorderArray:[myDict allKeys] toArray:referenceArray])
Actually you have a reference array in order manner then why you have to add to one more array.So i guess this approach is not good.Please consider my opinion.
Although #GenralMike 's answer works a breeze, it could be optimized by leaving off the unnecessary code as follows:
1) Keep an array to hold reference to the dictionary keys in the order they are added.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (id object in someArray) {
[yourDictionary setObject:object forKey:someKey];
[referenceArray addObject:someKey]; // add key to reference array
}
2) Now the "referenceArray" holds all of the keys in order, So you can retrieve objects from your dictionary in the same order as they were originally added to the dictionary.
for (NSString *key in referenceArray){
//get object from dictionary in order
id object = [yourDictionary objectForKey:key];
}