I want a count on an array where a sub array meets a condition.
I thought I could do this, but I can't.
NSLog(#"%d",[[_sections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[[obj objectAtIndex:4] isEqualToString:#"1"];
}] count]);
enumerateObjectsUsingBlock: doesn't return anything. I'd bet that code won't even compile (and, as your comment states, auto-completion doesn't work -- and it shouldn't).
Use NSArray's indexesOfObjectsPassingTest:
and take the count of the resulting NSIndexSet.
Documented here.
bbum is right; you should use indexesOfObjectsPassingTest. It's more straightforward.
But you could use enumerateObjectsUsingBlock to count test-passers, like this:
NSArray *sections = [NSArray arrayWithObjects:#"arb", #"1", #"misc", #"1", #"extra", nil];
NSMutableArray *occurrencesOf1 = [NSMutableArray array];
[sections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([(NSString*)obj isEqualToString:#"1"])
[occurrencesOf1 addObject:obj];
}];
NSLog(#"%d", [occurrencesOf1 count]); // prints 2
It's inefficient because it requires that extra mutable array.
(So you should check bbum's answer as the accepted one -- but I'm new at block functions too, and appreciated the puzzle.)
It's faster to use a for loop (and, IMO, more readable):
NSLog(#"%lu", (unsigned long)[self countSectionsWithValue:#"1" atIndex:4]);
// ...
}
// ...
- (NSUInteger) countSectionsWithValue:(NSString *)value atIndex:(NSInteger)idx
{
NSUInteger count = 0
for (id section in _sections)
{
if ([[section objectAtIndex:idx] isEqualToString:value])
{
count++;
}
}
return count;
}
Also note that I used the proper %lu format and (unsigned long) type in the NSLog. %d isn't descriptive and doesn't act the same in all scenarios.
Related
I have an array, NSMutableArray *stringArray that looks like this
stringArray =
[0]String1
[1]String2
[2]String3
[3]String4
[4]String5
[5]String6
How would I go about splitting this array into two arrays based on even/odd indexes?
Example:
NSMutableArray *oddArray = ([1], [3], [5]);
NSMutableArray *evenArray = ([0], [2], [4]);
Thanks in advance!
create two mutable arrays, use enumerateObjectsWithBlock: on the source array and check idx % 2 to put it into first or second array
Using the ternary operator:
NSArray *array = #[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12];
NSMutableArray *even = [#[] mutableCopy];
NSMutableArray *odd = [#[] mutableCopy];
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
NSMutableArray *evenOrOdd = (idx % 2) ? even : odd;
[evenOrOdd addObject:object];
}];
If you like super compact code you could use the ternary operator like
[((idx % 2) ? even : odd) addObject:object];
If you want to split the array to N arrays, you can do
NSArray *array = #[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12];
NSArray *resultArrays = #[[#[] mutableCopy],
[#[] mutableCopy],
[#[] mutableCopy]];
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[resultArrays[idx % resultArrays.count] addObject:object];
}];
In Objective-C Categories should come to your mind to create re-uasable code:
#interface NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N;
#end
#implementation NSArray (SplittingInto)
-(NSArray *)arraysBySplittingInto:(NSUInteger)N
{
NSAssert(N > 0, #"N cant be less than 1");
NSMutableArray *resultArrays = [#[] mutableCopy];
for (NSUInteger i =0 ; i<N; ++i) {
[resultArrays addObject:[#[] mutableCopy]];
}
[self enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[resultArrays[idx% resultArrays.count] addObject:object];
}];
return resultArrays;
}
#end
Now you can do
NSArray *array = [#[#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12] arraysBySplittingInto:2];
array contains
(
(
1,
3,
5,
7,
9,
11
),
(
2,
4,
6,
8,
10,
12
)
)
Create two NSIndexSets, one for the even indexes and one for the odd, then use objectsAtIndexes: to extract the corresponding slices of the array.
There are following ways you can achieve that:-
The first and second one solution are already mentioned by the above two. Below are the implementation of the same:-
//First Solution
NSArray *ar=#[#"1",#"2",#"3",#"4",#"5"];
NSMutableArray *mut1=[NSMutableArray array];
NSMutableArray *mut2=[NSMutableArray array];
[ar enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
if (idx%2==0)
{
[mut1 addObject:object];
}
else
{
[mut2 addObject:object];
}
}];
//Second Solution
NSMutableIndexSet *idx1 = [NSMutableIndexSet indexSet];
NSMutableIndexSet *idx2 = [NSMutableIndexSet indexSet];
for (NSUInteger index=0; index <ar.count(); index++)
{
if(index%2==0)
{
[idx1 addIndex:index];
}
else{
[idx2 addIndex:index];
}
}
NSArray *evenArr=[ar objectsAtIndexes:idx1];
NSArray *oddArr=[ar objectsAtIndexes:idx2];
NSLog(#"%#",evenArr);
NSLog(#"%#",oddArr);
Got some time for benchmarking and it turns out that when the input array has more than 10 million, it’s faster to use parallel execution.
Here is the concurrent solution that enumerates the input array twice to prevent race conditions on the output arrays.
static NSArray * concurrent(NSArray *input) {
NSUInteger capacity = input.count / 2;
NSArray *split = #[
[NSMutableArray arrayWithCapacity:capacity],
[NSMutableArray arrayWithCapacity:capacity],
];
[split enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(NSMutableArray *output, NSUInteger evenOdd, BOOL *stop) {
[input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
if (index % 2 == evenOdd) {
[output addObject:object];
}
}];
}];
return split;
}
I consider this to be the best serial solution, so I used it for benchmarking:
static NSArray * serial(NSArray *input) {
NSUInteger capacity = input.count / 2;
NSMutableArray *even = [NSMutableArray arrayWithCapacity:capacity];
NSMutableArray *odd = [NSMutableArray arrayWithCapacity:capacity];
[input enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
NSMutableArray *output = (index % 2) ? odd : even;
[output addObject:object];
}];
return #[ even, odd ];
}
Results
1 million elements
Serial: 54.081 ms
Concurrent: 65.958 ms (18% worse)
10 million elements
Serial: 525.851 ms
Concurrent: 412.691 ms (27% better)
100 million elements
Serial: 5244.798 ms
Concurrent: 4137.939 ms (27% better)
Average of 5 runs.
Input filled with NSNumbers.
Fastest smallest optimization -Os.
I've been trying for a few hours to work out an algorithm that can achieve the following conditions:
Take an arbitrary number, n arrays (each populated with strings), where each array will be a figurative 'parent' to the subsequent array
For each object (a string) in an array, combine that string with subsequent arrays' strings
Add the combination to an (just one) array
Repeat for all linear combinations of each object
I think this is best explained with an example:
e.g. For three arrays
NSArray *input =
#[[#"cat",#"dog",#"mouse"],[#"apple",#"banana"],[#"green"]]
produce an output that goes something like this:
#[
#"catapplegreen",
#"catbananagreen",
#"dogapplegreen",
#"dogbananagreen",
#"mouseapplegreen",
#"mousebananagreen"
];
I've tried nesting for loops but can't think of a way of allowing there to be an arbitrary amount of loops, as there needs to be one loop or 'level' per array in the input.
If anyone has any advice (even just pointers of what to look into to tackle this problem) I'd be most grateful.
So basically what I think you want to do it a Depth-First Traversal of your data.
Which you could do with a function such as
- (void)DepthFirstOnString:(NSString *)string children:(NSArray *)children {
if (children.count < 1) {
// You're finished
NSLog(#"%#", string);
return;
} else {
// Keep going
NSArray *next = children[0];
NSMutableArray *remaining = [children mutableCopy];
[remaining removeObject:next];
[next enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSMutableString *currentString = [string mutableCopy];
[currentString appendString:obj];
[self DepthFirstOnString:currentString children:remaining];
}];
}
}
being called by this code:
NSArray *input = #[#[#"cat",#"dog",#"mouse"], #[#"apple",#"banana"], #[#"green"]];
NSArray *first = input[0];
NSMutableArray *remaining = [input mutableCopy];
[remaining removeObject:first];
[first enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self DepthFirstOnString:obj children:remaining];
}];
Not the neatest code but hopefully gives you an idea of where to take it.
My challenge this week has been to come to terms with blocks in objective-c. There is something about the syntax that does my head in. Getting there.
I have the following code to achieve a merge of two arrays in a specific way (see comment in code below).
NSArray *keys = #[#"name", #"age"];
NSArray *data = #[
#[#"mark", #"36 years"],
#[#"matt", #"35 years"],
#[#"zoe", #"7 years"]
];
// desired outcome is
// # { #"name" : #[#"mark", #"matt", #"zoe"],
// #"age" : #[#"36 years", #"35 years", #"7 years"]
// }
NSMutableArray *mergedData = [[NSMutableArray alloc] initWithCapacity:keys.count];
for (NSString *key in keys) {
NSLog(#"key: %#", key);
NSInteger keyIndex = [keys indexOfObject:key];
NSMutableArray *dataItemsForKey = [[NSMutableArray alloc] initWithCapacity:data.count];
for (NSArray *row in data) {
// double check the array count for row equals the expected count for keys - otherwise we have a 'match up' issue
if (row.count == keys.count) {
[dataItemsForKey addObject:[row objectAtIndex:keyIndex]];
}
}
[mergedData addObject:[NSDictionary dictionaryWithObject:dataItemsForKey forKey:key]];
}
NSLog (#"mergedData: %#", mergedData);
While this code works fine, in the interest of my challenge and learning, I was wondering if there is a more 'elegant' (aka less code, easier to read) way to do this using enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) ??
I can't quite see a way to make it work, but in the interests of self-education, wonder if those more learned in blocks and arrays may have a more elegant solution.
The first issue that I notice is that you are asking for the index of the current object while enumerating the array. This is a waste of operations, because at every loop iteration you have to look over all array elements (potentially O(N)) to find where the object is.
You could instead do this:
for(NSUInteger i=0; i<keys.count; i++)
{
NSString* key= keys[i];
<Rest of the code>
}
Or just keep track of the index manually incrementing it:
NSUInteger i=0;
for (NSString *key in keys)
{
<Your code>
i++;
}
Or like you wanted, with enumerateObjectsUsingBlock:, which is IMO the most elegant way to do it in this case. Here is an example:
NSMutableDictionary* dict=[NSMutableDictionary new];
[keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
NSMutableArray* fields=[NSMutableArray new];
for(NSArray* array in data)
{
[fields addObject: array[idx]];
}
[dict setObject: fields forKey: obj];
}];
In the case you haven't understood how it works, here is a further explanation:
This way at every execution of the block you can know which is the current object (obj) and it's index (idx). stop is just used to stop enumerating the array, but you don't need it in this case (say that you want to stop the enumeration, you set *stop=YES). In my code I just took every element at the index idx of data, and build an array which is the value that I put into the dictionary, that has obj (what you called key in your code) as key. For any further doubt feel free to ask any clarification through a comment.
The first thing to say is your code does not produce the desired output. You get an array with two dictionaries each with one key.
One way to solve the problem is like this:
NSMutableDictionary* mergedData = [[NSMutableDictionary alloc] init];
[keys enumerateObjectsUsingBlock: ^(id key, NSUInteger keyIndex, BOOL *stop)
{
NSMutableArray* keyValues = [[NSMutableArray alloc] init];
for (NSArray* row in data)
{
[keyValues addObject: [row objectAtIndex: keyIndex]];
}
[mergedData setObject: keyValues forKey: key];
}];
The above will throw an exception if a row doesn't have enough objects in it. You could either check it beforehand or allow the program to crash, it's up to you.
I have this method which basically adds the letters to the contents in an array (demonstration)
- (NSMutableArray *) addTheLetterZ:(NSArray *)array {
NSMutableArray *addedLetterArray = [[NSMutableArray alloc] init];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[addedLetterArray addObject:[NSString stringWithFormat:#"%# Y", obj]];
}];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[addedLetterArray addObject:[NSString stringWithFormat:#"%# Z", obj]];
}];
return addedLetterArray;
}
I have 2 doubts:
Can any method be converted to a macro?
Is there a general rule of thumb to follow when converting a function to a macro?
I have looked at other questions but even in their solutions i get an error when i try the do-loop solution to convert this method to a macro.
Do you mean C macro?
#define macro_name(array, addedLetterArray, str) \
[(array) enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { \
[(addedLetterArray) addObject:[NSString stringWithFormat:(str), obj]]; \
}];
- (NSMutableArray *) addTheLetterZ:(NSArray *)array {
NSMutableArray *addedLetterArray = [[NSMutableArray alloc] init];
macro_name(array, addedLetterArray, #"%# Y")
macro_name(array, addedLetterArray, #"%# Z")
return addedLetterArray;
}
right?
The link you posted is the correct way to write a multi-line macro. To answer your questions:
Any number of lines of code can be converted into a macro, including a method. A macro is like a cut and paste in the editor.
There are a lot of pitfalls in using macros. This reference describes at least 4 ways macros are evil.
If you are having problems the best approach is to post your attempt at "macro-fication".
However: There is almost no case that you should use a macro to replace a function or method call. Most of the time macros are used they are used for the wrong reasons. For example if you are repeating a bunch of code that is only a few lines long and you think it will be more efficient to replace these lines with a macro instead of a function call you will generally be wrong. Modern compilers are very efficient at determining when functions should be inlined and effectively giving you the same advantages of a macro without the disadvantages. Macros have no compiler support for things like type checking, are error prone, and are harder to write.
I'm not sure what you had in mind, but here is an example from your own code on how you could use functions rather than macros:
void AddLetterToArray(NSArray *array, NSMutableArray *addedLetterArray, NSString *aLetter)
{
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[addedLetterArray addObject:[NSString stringWithFormat:#"%# %#", obj, aLetter]];
}];
}
- (NSMutableArray *) addTheLetterZ:(NSArray *)array {
NSMutableArray *addedLetterArray = [[NSMutableArray alloc] init];
AddLetterToArray(array, addedLetterArray, #"Y");
AddLetterToArray(array, addedLetterArray, #"Z");
// ...
return addedLetterArray;
}
I am trying to filter out an array of strings based on their length. I'm completely new to Objective C and OOP in general.
wordList=[[stringFile componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]] mutableCopy];
for (int x=0; x<[wordList count]; x++) {
if ([[wordList objectAtIndex:x] length] != 6) {
[wordList removeObjectAtIndex:x];
}else {
NSLog([wordList objectAtIndex:x]);
}
}
for (int x=0; x<[wordList count]; x++) {
NSLog([wordList objectAtIndex:x]);
}
The NSLog in the else statement will only output 6 letter words, but the second NSLog outputs the entire array. What am I missing here? Also any general pointers to clean up/improve the code are appreciated.
Depending on what you feel is the easiest to understand you could either filter the array with a predicate or iterate over the array and remove objects. You should chose the approach that you have easiest to understand and maintain.
Filter using a predicate
Predicates are a very concise way of filtering array or sets but depending on your background they may feel strange to use. You could filter your array like this:
NSMutableArray * wordList = // ...
[wordList filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSString *word = evaluatedObject;
return ([word length] == 6);
}]];
Enumerating and removing
You cannot modify the array while enumerating it but you can make a note of all the items what you want to remove and remove them all in a batch after having enumerated the entire array, like this:
NSMutableArray * wordList = // ...
NSMutableIndexSet *indicesForObjectsToRemove = [[NSMutableIndexSet alloc] init];
[wordList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *word = obj;
if ([word length] != 6) [indicesForObjectsToRemove addIndex:idx];
}];
[wordList removeObjectsAtIndexes:indicesForObjectsToRemove];
The problem with your code is that when you remove an item at index x and move to the next index x++, the item that was at x+1 is never examined.
The best way of filtering a mutable array is using the filterUsingPredicate: method. Here is how you use it:
wordList=[[stringFile
componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]]
mutableCopy];
[wordList filterUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) {
return [evaluatedObject length] == 6; // YES means "keep"
}]];