elegant comparing strings in two NSArrays - objective-c

Dear all. We have 2 arrays(currentCarriers and companyList with strings inside. A final solution have to be array, which exclude same string from first array.
Bellow is a my solutions, but probably two for loops is not like cocoa style. Maybe somebody can suggest something better?
for (NSString *carrier in currentCarriers) {
for (NSString *company in companyList)
{
if ([company isEqualToString:carrier]) [removedCompanies addObject:company]; }
}
NSMutableArray *companiesForAdd = [NSMutableArray arrayWithArray:companyList];
[companiesForAdd removeObjectsInArray:removedCompanies];

Turn one list into a mutable array and then use removeObjectsInArray:, as in:
foo = [NSMutableArray arrayWithArray:currentCarriers];
[foo removeObjectsInArray:companyList];
// foo now contains only carriers that are not in the company list.
Edit:
Alternative with set difference (but possible slower in most cases due to the copying/allocating):
NSMutableSet *foo = [NSMutableSet setWithArray:currentCarriers];
[foo minusSet:[NSSet setWithArray:companyList]];
This might be faster for bigger lists, but you lose the ordering (if any).

I suppose you can get rid of the inner loop using -containsObject: method of NSArray, something like
for (NSString *carrier in currentCarriers) {
if ([companyList containsObject:carrier])
[removedCompanies addObject:company];
}
NSMutableArray *companiesForAdd = [NSMutableArray arrayWithArray:companyList];
[companiesForAdd removeObjectsInArray:removedCompanies];

I can suggest two options
If you worry about performance, then you can sort arrays and then create a result in one pass.
If you don't worry, then just filter the currentCarriers using [companyList containsObject:]

Related

Objective C Array of Array of Strings

I'm trying to make an array of array of strings so that I can eventually pull out something like ArrayOfArrays[0][1] = "hi".
NSString *ArrayOne[] = {#"hello", #"hi"};
NSString *ArrayTwo[] = {#"goodbye", #"bye"};
NSArray *ArrayOfArrays[] = {#[*ArrayOne, *ArrayTwo]};
However when I try to do this, I get an error: Initializer element is not a compile-time constant.
I've read that this is because I'm creating an array with dynamic values, though it should be static. Not sure how to work around this.
Any advice on making an array of array of strings?
Use NSArray, or rather NSMutableArray if you want to modify it after creation:
NSMutableArray *arrayOne = [#[#"hello", #"hi"] mutableCopy];
NSMutableArray *arrayTwo = [#[#"goodbye", #"bye"] mutableCopy];
NSMutableArray *arrayOfArrays = [#[arrayOne, arrayTwo] mutableCopy];
There are other ways to initialise it, but this is the only way that allows you to use Objective-C literal syntax.
You cannot store plain ol' C arrays within an Objective-C collection class as your code attempts to do.
You wrote:
it should be static
if this is what you want then your use of C arrays is quite valid, you just got the syntax wrong. You can use:
NSString *arrayOfArrays[][2] =
{ {#"hello", #"hi"},
{#"goodbye", #"bye"},
};
Important: The 2 is the number of elements in the inner array, you do not change it when adding further pairs.
This will give you a compile-time static array.
If what you are making is a map from one word to another you might be better off with a dictionary, e.g.:
NSDictionary *wordMap =
#{ #"hello" : #"hi",
#"goodbye" : #"bye"
};
and accessing an element becomes:
wordMap[#"hello"];
Note: the dictionary "constant" here is actually executed code; the C array version can appear as a global or local initialiser, while the dictionary initialisation must be done in a method/function - but it can assign to a global.
HTH
NSArray *array = #[
#[[ #"hello", #"hi" ] mutableCopy],
#[[ #"goodbye", #"bye" ] mutableCopy],
];
NSLog(#"%# is short for %#", array[0][1], array[0][0]);
Output: hi is short for hello

Find string in array

I have a fun challenging problem. So I have a mutable array that contains all of my items. I have a textfield that **might have one or two of these items if the person types them in. **
items= [[NSArray alloc]initWithObjects:#"apple", #"orange", #"pear", nil];
items2= [[NSArray alloc]initWithObjects:#"cheese", #"milk", #"eggs", nil];
Allitems= [NSMutableArray array];
[Allitems addObjectsFromArray:items];
[Allitems addObjectsFromArray:items2];
NSArray*WORDS =[Textfield componentsSeparatedByString:#" "];
I am trying to detect what specific words from **Allitems are in the textfield. (If the textfield contains any string from ALLitems, how can I find what specific string?**
for (int i = 0; i < [Allitems count]; i++)
{
NSString *grabstring;
grabstring=[Allitems objectAtIndex:i];
if (textfield isEqualto:grabstring){
?????
pull that specific string from allitems.
}
}
You want the intersection of two sets:
NSMutableSet* intersectionSet = [NSMutableSet setWithArray:Allitems];
[intersectionSet intersectSet:[NSSet setWithArray:WORDS]];
NSArray* intersectionArray = [intersectionSet allObjects];
After this intersectionArray contains the items that are present in both Allitems and WORDS.
BTW, why do you capitalise variable names in a non-standard and inconsistent manner? Why not just allItems and words?
As #Arkku suggests: It's better to switch the arrays. In your example it does not matter much, but in case Allitems were (very) big, you can save (a lot of) memory and CPU usage:
NSMutableSet* intersectionSet = [NSMutableSet setWithArray:WORDS];
[intersectionSet intersectSet:[NSSet setWithArray:Allitems]];
NSArray* intersectionArray = [intersectionSet allObjects];
There are a various ways of doing it, each with different pros and cons. Let's have the following (consistently capitalized) variables in common for each case:
NSArray *allItems = #[ #"apple", #"orange", #"pear", #"cheese", #"milk", #"egg" ];
NSString *textFieldText = #"CHEESE ham pear";
NSArray *words = [textFieldText.lowercaseString componentsSeparatedByString:#" "];
NSPredicate
NSArray *matchingItems = [allItems filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"SELF IN %#", words]];
This is perhaps the shortest (in lines of code) way, but not the most performant if allItems can be very long as it requires traversing all of it.
Iteration
Of course you could also simply iterate over the collection and do the matching manually:
NSMutableArray *matchingItems = [NSMutableArray array];
for (NSString *item in allItems) {
if ([words containsObject:item]) {
[matchingItems addObject:item];
}
}
Again requires traversing all of allItems (although you could break the iteration if all words are matched).
In addition to the for loop there are of course many other ways for iteration, e.g., enumerateObjectsUsingBlock:, but they are unlikely to have any advantage here.
NSSet
NSSet is often a good option for this kind of matching since testing set membership is faster than with NSArray. However, if using the most straightforward method intersetSet: (in NSMutableSet) care must be taken to not inadvertently create a large mutable set only to discard most of its items.
If the order of allItems does not matter, the best way would be to change it from an array into a set and always keep that set around, i.e., instead of creating the array allItems, you would create an NSSet:
NSSet *setOfAllItems = [NSSet setWithArray:allItems];
Or if it needs to be mutable:
NSMutableSet *setOfAllItems = [NSMutableSet set];
[setOfAllItems addObjectsFromArray:items1];
[setOfAllItems addObjectsFromArray:items2];
Then, when you have that set, you create a temporary mutable set out of words (which is presumably always the smaller set):
NSMutableSet *setOfMatches = [NSMutableSet setWithArray:words];
[setOfMatches intersectSet:setOfAllItems];
NSArray *matchingItems = setOfMatches.allObjects;
This would be likely be the most performant solution if setOfAllItems is large, but note that the matches will then need to be exact. The other methods are more easily adapted to things like matching the strings in words against fields of objects or keys in a dictionary (and returning the matched objects rather than the strings). In such a case one possibility to consider would be an NSDictionary mapping the words to match to the objects to return (also fast to then iterate over words and test for membership in the dictionary).
Conversion to string
And, since the question included conversion of matches to a string:
[matchingItems componentsJoinedByString:#", "]
In the example case this would result in the string "pear, cheese" (or possibly "cheese, pear" if using sets).

Objective-C Fast Enumeration: checking for BOOL

Scenario = I need to loop through an array and find how many "unread" there are and count how many to display to the user.
What I'm Looking For = something like this (this is not my real code)
for (NSDictionary *dic in self.objects) {
[unreadCountArray addObject:dic[#"wasRead"]];
for (YES in unreadCountArray) {
//statements
}
}
Question = Does anyone know how to loop through and find all of the YES booleans?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"wasRead = YES"];
NSArray *arr = [array filteredArrayUsingPredicate:predicate];
Can sort a thousand objects in 0.0004 seconds.
Then just do:
for (NSDictionary *object in arr) {
//Statements
}
Edit: actually after further experimentation, using fast-enumeration is about four times faster, about 0.0001, which if scaled to 100000 objects can be much, much faster.
NSMutableArray *test = [NSMutableArray array];
for (NSDictionary *dict in array)
if ([dict[#"theKey"] boolValue])
[test addObject:dict];
So for sorting, fast-enumeration is actually faster but for just a couple hundred objects, the performance increase is negligible.
And please before asking questions like this and getting downvotes, those could have been completely avoided by checking the documentation. Like this article and this article.
If you have an array of dictionaries, and you want to filter them, then filteredArrayUsingPredicate: is the method to use.
You can create a predicate using the key from your dictionary (predicateWithFormat:).
This will then give you an array of dictionaries that match the conditions in your predicate.
No sample code, I'm answering this on a phone.

Get list of Values for an NSArray of NSDictionary

I've got the following NSArray :
NSArray myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
I want the list of value of my dictionaries :
#[#"5 min",#"15 min",#"30 min",#"1 hour"]
And the list of key of my dictionaries :
#[#300, #900, #1800, #3600]
What is the best way to do that ? I was thinking about predicate, but I don't know how to use it ?
Without some code to show how you'd want to go about this it is difficult to be sure exactly what you are after, and there is a bit of confusion in the question.
First, a predicate is exactly that - a statement that can be proven true or false. Predicates are hence used in logic expressions, including those employed implicitly in database queries - such as Core Data.
That is not what you want, if I read your question correctly. What you want is to reduce the complexity of your data model, removing some excess (one would hope) information in the process. A sort of flattening of an array of dictionaries.
Fair enough.
I can also see how the confusion with predicates came about - they are most often constructed using Key-Value Coding. KVC, as it is also known, is a very powerful technique that can accomplish what you are after. It just does not have much to do with a logic statement.
Having cleared that up, with KVC you can do what you want, and with minimal fuss. It goes like this:
NSArray *values = [myArray valueForKeyPath: #"#unionOfArrays.#allValues"];
NSArray *keys = [myArray valueForKeyPath: #"#unionOfArrays.#allKeys"];
A brief explanation might be in order:
The results that we want are
All the values (or keys) of each dictionary, obtaining an array of arrays of values (or keys)
Then we want to flatten these arrays into a single array.
To obtain all values (or keys) from a dictionary using KVC, the special key is #allValues or #allKeys, respectively.
The #unionOfArrays operator makes a union of the arrays obtained from the expression that follows it, i.e., flattens it into the array you wanted.
The price you pay for this coding simplicity is that you have to use KVC key paths with collection operators, which are just strings in your code. You therefore lose any help from the compiler with syntax and it doesn't check that the keys you enter exist in the objects. Similarly, the debugger and error messages are unhelpful if you mistype or use the wrong operator, for instance.
You can use dictionary property allValues to get all values of dictionary.
Try this code in your case
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *arr = [NSMutableArray array];
for (NSDictionary *dict in myArray) {
[arr addObject:[[dict allValues] objectAtIndex:0]];
}
NSLog(#"%#",arr);
Note : Make sure you have only one value in each dictionary.
it will return
[
5 min,
15 min,
30 min,
1 hour
]
#johnyu's answers is technically correct, but I don't see any reason to include the secondary loop, especially if the data structure will remain the same.
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *arrayOfValues = [NSMutableArray new];
NSMutableArray *arrayOfKeys = [NSMutableArray new];
for (NSDictionary *dictionary in myArray) {
[arrayOfValues addObject:dictionary.allValues[0]];
[arrayOfKeys addObject:dictionary.allKeys[0]];
}
NSLog(#"%#",arrayOfKeys);
NSLog(#"%#",arrayOfValues);
Try this:
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
NSMutableArray *valueArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in myArray) {
for (NSString *key in dictionary) {
[keyArray addObject:key];
[valueArray addObject:[dictionary objectForKey:key]];
}
}

best way to populate NSArray in this algorithm

I intend to make a program that does the following:
Create an NSArray populated with numbers from 1 to 100,000.
Loop over some code that deletes certain elements of the NSArray when certain conditions are met.
Store the resultant NSArray.
However the above steps will also be looped over many times and so I need a fast way of making this NSArray that has 100,000 number elements.
So what is the fastest way of doing it?
Is there an alternative to iteratively populating an Array using a for loop? Such as an NSArray method that could do this quickly for me?
Or perhaps I could make the NSArray with the 100,000 numbers by any means the first time. And then create every new NSArray (for step 1) by using method arraywithArray? (is it quicker way of doing it?)
Or perhaps you have something completely different in mind that will achieve what I want.
edit: replace NSArray with NSMutableArray in above post
It is difficult to tell in advance which method will be the fastest. I like the block based functions, e.g.
NSMutableArray *array = ...; // your mutable array
NSIndexSet *toBeRemoved = [array indexesOfObjectsPassingTest:^BOOL(NSNumber *num, NSUInteger idx, BOOL *stop) {
// Block is called for each number "num" in the array.
// return YES if the element should be removed and NO otherwise;
}];
[array removeObjectsAtIndexes:toBeRemoved];
You should probably start with a correctly working algorithm and then use Instruments for profiling.
You may want to look at NSMutableIndexSet. It is designed to efficiently store ranges of numbers.
You can initialize it like this:
NSMutableIndexSet *set = [[NSMutableIndexSet alloc]
initWithIndexesInRange:NSMakeRange(1, 100000)];
Then you can remove, for example, 123 from it like this:
[set removeIndex:123];
Or you can remove 400 through 409 like this:
[set removeIndexesInRange:NSMakeRange(400, 10)];
You can iterate through all of the remaining indexes in the set like this:
[set enumerateIndexesUsingBlock:^(NSUInteger i, BOOL *stop) {
NSLog(#"set still includes %lu", (unsigned long)i);
}];
or, more efficiently, like this:
[set enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) {
NSLog(#"set still includes %lu indexes starting at %lu",
(unsigned long)range.length, (unsigned long)range.location);
}];
I'm quite certain it will be fastest to create the array using a c array, then creating an NSArray from that (benchmark coming soon). Depending on how you want to delete the numbers, it may be fastest to do that in the initial loop:
const int max_num = 100000;
...
id *nums = malloc(max_num * sizeof(*nums));
int c = 0;
for(int i = 1; i <= max_num; i++) {
if(!should_skip(i)) nums[c++] = #(i);
}
NSArray *nsa = [NSArray arrayWithObjects:nums count:c];
First benchmark was somewhat surprising. For 100M objects:
NSArray alloc init: 8.6s
NSArray alloc initWithCapacity: 8.6s
id *nums: 6.4s
So an array is faster, but not by as much as I expected.
You can use fast enumeration to search through the array.
for(NSNumber item in myArrayOfNumbers)
{
If(some condition)
{
NSLog(#"Found an Item: %#",item);
}
}
You might want to reconsider what you are doing here. Ask yourself why you want such an array. If your goal is to manipulate an arbitrarily large collection of integers, you'll likely prefer to use NSIndexSet (and its mutable counterpart).
If you really want to manipulate a NSArray in the most efficient way, you will want to implement a dedicated subclass that is especially optimized for this kind of job.