How to get indexes from NSIndexset into an NSArray in cocoa? - objective-c

I'm getting the select items from a table view with:
NSIndexSet *selectedItems = [aTableView selectedRowIndexes];
what's the best way to get the indexes in a NSArray object?

Enumerate the set, make NSNumbers out of the indexes, add the NSNumbers to an array.
That's how you'd do it. I'm not sure I see the point in transforming a set of indexes into a less efficient representation, though.
To enumerate a set, you have two options. If you're targeting OS X 10.6 or iOS 4, you can use enumerateIndexesUsingBlock:. If you're targeting earlier versions, you'll have to get the firstIndex and then keep asking for indexGreaterThanIndex: on the previous result until you get NSNotFound.

NSIndexSet *selectedItems = [aTableView selectedRowIndexes];
NSMutableArray *selectedItemsArray=[NSMutableArray array];
[selectedItems enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
[selectedItemsArray addObject:[NSNumber numberWithInteger:idx]];
}];

With swift you can do the following
extension NSIndexSet {
func toArray() -> [Int] {
var indexes:[Int] = [];
self.enumerateIndexesUsingBlock { (index:Int, _) in
indexes.append(index);
}
return indexes;
}
}
then you can do
selectedItems.toArray()

Here is the sample code:
NSIndexSet *filteredObjects = [items indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {do testing here}];
NSArray *theObjects = [theItems objectsAtIndexes:filteredObjects]
Availability
Available in iOS 2.0 and later.

I did it by creating a category on NSIndexSet. This kept it small and efficient, requiring very little code on my part.
My interface (NSIndexSet_Arrays.h):
/**
* Provides a category of NSIndexSet that allows the conversion to and from an NSDictionary
* object.
*/
#interface NSIndexSet (Arrays)
/**
* Returns an NSArray containing the contents of the NSIndexSet in a format that can be persisted.
*/
- (NSArray*) arrayRepresentation;
/**
* Returns an array of NSNumber objects representing each index in the set.
*/
- (NSArray<NSNumber*>*) arrayOfNumbersRepresentation;
/**
* Initialises self with the indexes found wtihin the specified array that has previously been
* created by the method #see arrayRepresentation.
*/
+ (NSIndexSet*) indexSetWithArrayRepresentation:(NSArray*)array;
#end
and the implementation (NSIndexSet_Arrays.m):
#import "NSIndexSet_Arrays.h"
#implementation NSIndexSet (Arrays)
/**
* Returns an NSArray containing the contents of the NSIndexSet in a format that can be persisted.
*/
- (NSArray*) arrayRepresentation {
NSMutableArray *result = [NSMutableArray array];
[self enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) {
[result addObject:NSStringFromRange(range)];
}];
return [NSArray arrayWithArray:result];
}
/**
* Returns an array of NSNumber objects representing each index in the set.
*/
- (NSArray<NSNumber*>*) arrayOfNumbersRepresentation {
NSMutableArray<NSNumber*> *result = [NSMutableArray array];
[self enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[result addObject:[NSNumber numberWithInteger:idx]];
}];
return [NSArray arrayWithArray:result];
}
/**
* Initialises self with the indexes found wtihin the specified array that has previously been
* created by the method #see arrayRepresentation.
*/
+ (NSIndexSet*) indexSetWithArrayRepresentation:(NSArray*)array {
NSMutableIndexSet *result = [NSMutableIndexSet indexSet];
for (NSString *range in array) {
[result addIndexesInRange:NSRangeFromString(range)];
}
return result;
}
#end

Related

Obj-C How to set a property on all elements of an array [duplicate]

This question already has an answer here:
Set bool property of all objects in the array
(1 answer)
Closed 6 years ago.
I'm trying to do something like this:
[array makeObjectsPerformSelector:#selector(setFoo:) withObject:[NSNumber numberWithBool:YES]];
where foo is a public property (type BOOL) of the class of the elements of array. However, this is not working, and I think it has to do with how I am passing the argument, because it works fine when i loop through the array and call [array[i] setFoo:YES] for each element of the array. I have also tried passing #YES and #"YES".
This:
#interface Blarg:NSObject
#property BOOL flag;
#end
#implementation Blarg
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSArray *a = #[[Blarg new], [Blarg new], [Blarg new]];
NSLog(#"Start %#", [a valueForKey:#"flag"]);
[a setValue:#YES forKey:#"flag"];
NSLog(#"Yes %#", [a valueForKey:#"flag"]);
[a setValue:#NO forKey:#"flag"];
NSLog(#"No %#", [a valueForKey:#"flag"]);
}
return 0;
}
Spews this:
2016-04-14 23:28:00.648 dfjkdf[767:101069] Start (
0,
0,
0
)
2016-04-14 23:28:00.650 dfjkdf[767:101069] Yes (
1,
1,
1
)
2016-04-14 23:28:00.650 dfjkdf[767:101069] No (
0,
0,
0
)
Is it right? Sort of. Generally, this isn't really a pattern you should use.
You're better off doing something like:
[a enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
Blarg *b = obj;
b.flag = YES;
}];
Or, better with a modern compiler:
[a enumerateObjectsUsingBlock:^(Blarg * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj.flag = YES;
}];
It is a couple of extra lines of code, but it is pedantically type correct and, thus, the compiler will scream if the code changes in the future to break it. Meta-programming hamstrings the compiler's ability to help you.
Use Key-Value Coding:
[array setValue:#YES forKey:#"foo"];
From the docs for NSArray's override of -setValue:forKey::
Invokes setValue:forKey: on each of the array's items using the specified value and key.

NSDictionary and comparing SetObjects

Hi I'm wondering how to compare the contents of the setObjects stored with keys in an NSDictionary: Here's my code I'm working with:
[INNumbers setObject:#"0,1,2,3,4,5,6,7" forKey:#"0"];
[INNumbers setObject:#"4,5,6,7,8,9,10,11" forKey:#"1"];
I'm wonder how I would compare the 2 keys and find if there are similar numbers like if 7 occurs in both?
//this will get the numbers in the keys for me
id number1 = [INNumbers objectForKey:#"0"];
id number2 = [INNumbers objectForKey:#"1"];
but I'm not sure how to compare what number1 and number2 retrieve.
If I was not force to use strings, I would do it this way:
NSArray *nums0 = #[#1,#2,#3];
NSArray *nums1 = #[#3,#4,#5];
NSMutableSet *intersection = [NSMutableSet setWithArray:nums0];
[intersection intersectSet:[NSSet setWithArray:nums1]];
NSArray *numsInCommon = [intersection allObjects];
numsInCommon will contain #3 that is the number that is in both arrays
In your example, the values stored in the dictionary are string literals, which are hard to parse and test for membership tests repeatedly. Perhaps you wanted to use NSSets:
#import <Foundation/Foundation.h>
int main()
{
NSSet *numbers1 = [NSSet setWithObjects:#0, #1, #2, nil];
NSSet *numbers2 = [NSSet setWithObjects:#0, #4, #5, nil];
if([numbers1 intersectsSet : numbers2]) {
NSLog(#"The two sets have at least one element in common!");
/* Let's obtain the intersection: */
NSSet *common = [numbers1 objectsPassingTest:^BOOL(id obj, BOOL *stop) {
if ([numbers2 containsObject:obj]) {
return YES;
} else {
return NO;
}
}];
for (id o in common) {
NSLog(#"%#", o);
}
}
return 0;
}

Compare two arrays with the same value but with a different order

I have 2 nsarray, with the same values but in different order.
NSArray * array1 = {0,1,2,3}
NSArray * array2 = {2,3,1,0}
I need a method to determinate if two arrays have the same values in a different order.
Kind of
-(BOOL) isSameValues:(NSArray*)array1 and:(NSArray*)array2;
You can use NSCountedSet for that purpose:
- (BOOL)isSameValues:(NSArray*)array1 and:(NSArray*)array2
{
NSCountedSet *set1 = [NSCountedSet setWithArray:array1];
NSCountedSet *set2 = [NSCountedSet setWithArray:array2];
return [set1 isEqualToSet:set2];
}
NSCountedSet is a collection of different objects, where each object has an associated counter with it. Therefore the result for
NSArray *array1 = #[#0,#1,#2,#3];
NSArray *array2 = #[#2,#3,#1,#0];
is YES, but for
NSArray *array1 = #[#1,#1,#3,#3];
NSArray *array2 = #[#3,#3,#3,#1];
the result is NO.
Update: this will not work if arrays have duplicate elements!
You could create two NSSets with those arrays and the compare them.
NSArray * array1 = #[#0,#1,#2,#3];
NSArray * array2 = #[#2,#3,#1,#0];
NSSet *set1 = [NSSet setWithArray:array1];
NSSet *set2 = [NSSet setWithArray:array2];
NSLog(#"result %#", [set1 isEqualToSet:set2] ? #"YES" : #"NO");
if ([[NSSet setWithArray:array1] isEqualToSet:[NSSet setWithArray:array2]]) {
// the objects are the same
}
Take total no of elements. Have a counter. And put double 'for loop' to parse through each and every element of each other. Increment the counter at each matching.
Note : This is valid when all elements are unique.
If different or you don't know, sort them and match one to one.
An other way would be to use a NSHashTable.
- (BOOL)array:(NSArray *)array1 containsTheSameObjectsAsArray:(NSArray *)array2 {
if (array1.count != array2.count) {
return NO;
}
NSHashTable *table = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory
capacity:array1.count];
for (NSObject *object in array1) {
[table addObject:object];
}
for (NSObject *object in array2) {
if (![table containsObject:object]) {
return NO;
}
}
return YES;
}
Note that NSHashTable requires iOS 6+

Would like NSArray valueForKey to return the array indices

I can create an NSArray that contains all the hash values of the objects in myArray like this:
NSArray *a = [myArray valueForKey:#"hash"];
What key do I pass to valueForKey: to get an array containing myArray's indices?
Say myArray has n items. I'd like to do something like this:
NSArray *a = [myArray valueForKey:#"index"];
NSArray * arrayWithNumbersInRange( NSRange range )
{
NSMutableArray * arr = [NSMutableArray array];
NSUInteger i;
for( i = range.location; i <= range.location + range.length; i++ ){
[arr addObject:[NSNumber numberWithUnsignedInteger:i];
}
return arr;
}
NSArray * indexArray = arrayWithNumbersInRange((NSRange){0, [myArray length]-1});
You can query an NSArray for the index of an object with indexOfObject:
NSArray *a = [myArray valueForKey:#"hash"];
NSInteger index = [myArray indexOfObject:a];
Create a superclass of NSArray which returns the index:
//
// CapArrayOfIndex.h
// Created by carmin politano on 7/26/12.
// no rights reserved
//
#interface CapArrayOfIndex : NSArray {
/*! Simulated constant array containing an array of NSNumber representing the index. */
NSUInteger iCount;
//*! NSNotFound causes bybass of index bounds testing. */
}
#pragma mark create
+ (CapArrayOfIndex*) withCount: (NSUInteger) aCount;
#end
and
//
// CapArrayOfIndex.mm
// Created by carmin on 7/26/12.
// no rights reserved
//
#import "CapArrayOfIndex.h"
#implementation CapArrayOfIndex
#pragma mark NSCopying
- (id) copyWithZone: (NSZone*) aZone {
/*! New allocation required because -count is a mutable property. */
return [CapArrayOfIndex withCount: self.count];
}
#pragma mark create
+ (CapArrayOfIndex*) withCount: (NSUInteger) aCount {
CapArrayOfIndex* zArray = self.alloc;
zArray->iCount = aCount;
return zArray;
}
#pragma mark NSArray
- (NSUInteger) count {
return iCount;
}
- (void) setCount: (NSUInteger) aCount {
iCount = aCount;
}
- (id) objectAtIndex: (NSUInteger) aIndex {
/*! My code performs a bounds test using unusual macros: return [ViaUInteger RaiseIfObjectAtIndex(nil, self, aIndex, nil)]; */
return [NSNumber numberWithInteger: aIndex];
}
#end

match NSString against NSString field of an array

I have an NSMutableArray called myObjectArray which contains and array of NSObjects called myObject. myObject has two fields (elements?) which are NSString's. like this:
#interface myObject : NSObject {
NSString * string1;
NSString * string2;
}
I have an NSMutableArray which contains about 50 of these objects, all with different string1's and string2's. then I have and independent NSString variable, called otherString;
Is there a fast way to access the myObject from myObjectArray whose string1 matches otherString?
i should say, this is what i have, but i wonder if there is a faster way:
-(void) matchString: {
NSString * testString = otherString;
for(int i=0; i<[myObjectArray count];i++){
myObject * tempobject = [myObjectArray objectAtIndex:i];
NSString * tempString = tempobject.string1;
if ([testString isEqualToString:tempString]) {
// do whatever
}
}
}
There are a few ways you can do this,
Using Predicates
NSPredicate * filterPredicate = [NSPredicate predicateWithFormat:#"string1 MATCHES[cd] %#", otherString];
NSArray * filteredArray = [myObjectArray filteredArrayUsingPredicate:filterPredicate];
Now filteredArray has all the myObject instances that have their string1 matching otherString.
Using indexOfObjectPassingTest:
NSUInteger index = [myObjectArray indexOfObjectPassingTest:^(BOOL)(id obj, NSUInteger idx, BOOL *stop){
myObject anObject = obj;
return [anObject.string1 isEqualToString:otherString];
}
If there is an object that satisfies the condition, index will point you to its index. Otherwise it will have the value NSNotFound.
You can also look at indexesOfObjectsPassingTest: if you want all the objects satisfying the condition.