I have an array of myObjects called arrayToFilter. One (element?) of myObject is an array of bezierpaths. I am comparing the bezierpath at a particular index (thispath) to a second path, and making filteredArray composed of only those objects in which the paths overlapped a certain amount (20%). I used indexedOfObjectsPassingTest to like this:
NSIndexSet * index = [[arrayToFilter objectsAtIndexes:index] indexesOfObjectsPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
return overlap>20;
}];
if ([index count] !=0){
filteredArray = [arrayToFilter objectsAtIndexes:index] ;
}
This works fine. But what i'd like to do is have filteredArray come out sorted with those object with the higher value for overlap coming out first. since overlap is calculated on the fly based on the current path and thispath, i don't know how to use any of the sorted array methods.
You can start off by creating an array of dictionaries containing both path and the overlap data. This will require some modification to your current approach where you search and extract over filter.
NSMutableArray * searchResults = [NSMutableArray array];
[arrayToSearch enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
MyObject * anobject = obj;
UIBezierPath * thispath = [anobject.allPaths objectAtIndex:i];
NSInteger overlap = [self percentPathBoxOverlap:path: thispath];
if ( overlap > 20 ) {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:anObject, #"object", [NSNumber numberWithInteger:overlap], #"overlap", nil];
[searchResults addObject:dictionary];
}
}];
Now you can sort this array using the overlap key of the dictionaries.
NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey:#"overlap" ascending:NO];
NSArray * sortedArray = [searchResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
Now sortedArray will have the sorted list of path and overlap information.
You'll have to sort the array first, then call indexesOfObjectsPassingTest for the sorted indices. sortedArrayUsingComparator: is one of the easier methods of sorting an array, it takes a block just like the indexesOfObjectsPassingTest method.
NSArray arrayToFilter = [originalArray sortedArrayUsingComparator: ^(id a, id b)
{
if (a.someValue > b.someValue) return NSOrderedAscending;
if (a.someValue < b.someValue) return NSOrderedDescending;
return NSOrderedSame;
}];
Then you can perform your existing filtering on the arrayToFilter
Related
I have the following categoryNames array.
And now, I have categoryTempElements and it has cName property. I need to know how to order categoryTempElements with a order of categoryNames.
UPDATE: I have added sortOrder property to Category object and tried the following but order does not change.
for (Category* a in categoryTempElements) {
int index = (int)[categoryNames indexOfObject:a.cName];
a.sortOrder = index;
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [categoryTempElements sortedArrayUsingDescriptors:sortDescriptors];
Another way could be without using sortedArrayUsingComparator, using two for-loops. Declare a new Mutable array called sortedCategoryElements and compare the categoryNames in categoryTempElements, If matches add it to a new array sortedCategoryElements:
NSMutableArray *sortedCategoryElements = [NSMutableArray new];
for (NSString *name in categoryNames) {
for (Category *category in categoryTempElements) {
if (name == category.cName) {
[sortedCategoryElements addObject:category];
break;
}
}
}
I tried with your set of data, it worked for me.
Hope it helps!
You need first convert your categoryNames array into dictionary with NSString key and NSNumber int value, the value will be the order in the array
//this is example code, this will be your first array (reference value array)
NSArray * array = #[#"prueba",#"prueba2",#"prueba3"];
//first you need convert this array in NSDictionary
NSMutableDictionary * arrayDict = [NSMutableDictionary dictionary];
int counter = 0;
for (NSString * value in array) {
if(arrayDict[value] == nil)
{
arrayDict[value] = [NSNumber numberWithInt:counter];
}
counter++;
}
After that then you can get the value and order with sortedArrayUsingComparator method, something like this
//this is an example of your second array categoryTempElements
NSArray * arrayOfObjs = #[[testObject testObjectWithName:#"prueba3"],[testObject testObjectWithName:#"prueba"],[testObject testObjectWithName:#"prueba2"]];
NSArray * sorted = [arrayOfObjs sortedArrayUsingComparator:^NSComparisonResult(testObject * _Nonnull obj1, testObject * _Nonnull obj2) {
if([((NSNumber*)arrayDict[obj1.cName]) intValue] < [((NSNumber*)arrayDict[obj2.cName]) intValue]){
return NSOrderedAscending;
}
if([((NSNumber*)arrayDict[obj1.cName]) intValue] > [((NSNumber*)arrayDict[obj2.cName]) intValue]){
return NSOrderedDescending;
}
return NSOrderedSame;
}];
for (testObject * obj in sorted) {
NSLog(#"%#",obj.cName);
}
And voila in sorted you will have your array of object sorted by your first array NSString order
Hope this helps
Create a dictionary with cName as the key and the Category object as the value. Then iterate over the categoryNames array, and build another array by using each item in categoryNames as the key. The resulting array should be sorted in the same order as categoryNames.
NSArray has a method sortedArrayUsingComparator: which sorts an array using the ordering implemented by the block you pass. This block, of type NSComparator, is passed references to two elements of your array and you must return the order of those two elements.
And now, I have categoryTempElements and it has cName property.
So your block will be passed two categoryTempElements, you need to access the cName property of each, and compare the resulting two values...
I need to know how to order categoryTempElements with a order of categoryNames
by the position, i.e. the index, of those values in your categoryNames array. The method indexOfObject: provides that index for you.
So put that together and your problem is solved.
HTH
I have NSMutableArray which stores NSDictionary. Consider following array which contain NSDictionary.
<__NSArrayM 0x7f9614847e60>(
{
"PARAMETER_KEY" = 1;
"PARAMETER_VALUE" = ALL;
},
{
"PARAMETER_KEY" = 2;
"PARAMETER_VALUE" = ABC;
},
{
"PARAMETER_KEY" = 3;
"PARAMETER_VALUE" = DEF;
},
{
"PARAMETER_KEY" = 4;
"PARAMETER_VALUE" = GHI;
},
{
"PARAMETER_KEY" = 5;
"PARAMETER_VALUE" = JKL;
}
)
I can find index of specific NSDictionary using following code.
int tag = (int)[listArray indexOfObject:dictionary];
But If I have PARAMETER_VALUE = GHI and using this value I want to find that dictionary index into array. I don't want to use for loop. Can I get index without for loop?
You can use indexOfObjectPassingTest method of NSArray:
[listArray indexOfObjectPassingTest:^BOOL(NSDictionary* _Nonnull dic, NSUInteger idx, BOOL * _Nonnull stop) {
return [dic[#"PARAMETER_VALUE"] isEqualToString:#"GHI"];
}];
Also, please consider using indexesOfObjectsPassingTest if you can have multiple dictionaries with the same PARAMETER_VALUE
You can add a category on NSArray like this (this does a type safety check as well; only array of dictionaries are processed):
- (NSInteger)indexOfDictionaryWithKey:(NSString *)iKey andValue:(id)iValue {
NSUInteger index = [self indexOfObjectPassingTest:^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
if (![dict isKindOfClass:[NSDictionary class]]) {
*stop = YES;
return false;
}
return [dict[iKey] isEqual:iValue];
}];
return index;
}
And then simply call indexOfDictionaryWithKey:andValue: directly on your array object to get the index.
Just in case if you want to get the dictionary object out of that array, add one more category in NSArray:
- (NSDictionary *)dictionaryWithKey:(NSString *)iKey andValue:(id)iValue {
NSUInteger index = [self indexOfDictionaryWithKey:iKey andValue:iValue];
return (index == NSNotFound) ? nil : self[index];
}
You can use NSPredicate for this purpose:
// Creating predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.PARAMETER_VALUE MATCHES %#",#"GHI"];
// Filtering array
NSArray *filteredArr = [arr filteredArrayUsingPredicate:predicate];
// If filtered array count is greater than zero (that means specified object is available in the array), checking the index of object
// There can be multiple objects available in the filtered array based on the value it holds (In this sample code, only checking the index of first object
if ([filteredArr count])
{
NSLog(#"Index %d",[arr indexOfObject:filteredArr[0]]);
}
Well, one has to enumerate in a way. Taking your requirement literally (no for loop), you can use fast enumeration. However, the task can be run concurrently, because you only need read access:
__block NSUInteger index;
[array enumerateObjectsWithOptions: NSEnumerationConcurrent
usingBlock:
^(NSDictionary *obj, NSUInteger idx, BOOL *stop)
{
if( [obj valueForKey:#"PARAMETER_VALUE" isEqualToString:#"GHI" )
{
index = idx;
*stop=YES;
}
}
I am trying to tackle the following sorting and separating, I have an array with IDs like 1,2,3,4,5,3,2,1.
Sorting that array with NSPredicate is quite straightforward but how can i also separate same IDs in separate sub-arrays like [[1,1][2,2,],[3,3],[4],[5]]? I guess one option is to loop the sorted array and compare previous index ids, but I am wondering if any helper function exist in iOS, I am currently reading about NSOrderedSet but cant seem to find if it can help.
In Swift , with functional programming:
let indexes = [1,2,3,4,5,3,2,1]
let notRepeatedIndexesSet = Set(indexes)
let notRepeatedIndexesArray = Array(notRepeatedIndexesSet).sorted(<)
let yourArray = notRepeatedIndexesArray.map{
number -> [Int] in
Array(count: indexes.filter { $0 == number }.count, repeatedValue:number)
}
A combination of ordered and counted sets:
NSArray *array = #[#1,#2,#3,#4,#5,#3,#2,#1];
NSOrderedSet *os = [[NSOrderedSet alloc] initWithArray:array];
NSCountedSet *cs = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *sortedArray = [#[] mutableCopy];
[os enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSMutableArray *countArray = [#[] mutableCopy];
for (int i = 0; i < [cs countForObject:obj]; ++i) {
[countArray addObject:obj];
}
[sortedArray addObject:[countArray copy]];
}];
It's hard to explain why I need index of duplicate elements in array. When I tried to fetch the index of element in traditional way it shows only one index, but I need to fetch the all index of duplicate values
for ex:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
int index = [array indexOfObject:element];
NSLog(#"index %d",index);
here if I try to fetch index of " one " it shows index is 0 but I need to get further indexes of one
You can fetch the index of duplicates like this:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isEqualToString:#"one"])
{
NSLog(#"index %d",idx);
}
}];
int i,count=0;
for (i = 0; i < [array count]; i++) {
if element == [array objectAtIndex:i] {
indices[count++] = i;
}
}
Declare an empty array indices, and indices will contain all the indices of the given element.
NSString *element = #"one";
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSIndexSet *matchingIndexes = [array indexesOfObjectsPassingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [obj isEqual:element];
}];
[matchingIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"%ld", (long)idx);
}];
Ultimately I don't think the NSArray methods are going to help you here, so you're going to have to write some pretty basic code. There is probably a cleaner answer, but here is a fairly simply solution to the problem.
This just goes through the array, and creates an NSDictionary for each unique number. It assumes the array is sorted as your example was, so simply checks the prior index's value against the current index to see if they have changed. When they change, it knows it's done with that value and saves the dictionary to an array.
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSString *priorString = array[0];
NSMutableDictionary *duplicatesByKey = [[NSMutableDictionary alloc] init];
NSMutableArray *indexesOfDuplicates = [[NSMutableArray alloc] init];
int index = 0;
for (NSString *string in array) {
if ([priorString isEqualToString:string]) {
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
} else {
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
indexesOfDuplicates = [[NSMutableArray alloc] init];
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
}
priorString = string;
index ++;
}
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
I hope that helps.
Use
NSCountedSet * countedSet = [NSCountedSet setWithArray: array];
and
NSSet * uncountedSet = [NSSet setWithArray: array];
-- to create a counted set from your array, and a conventional NSSet.
Then:
[countedSet minusSet: uncountedSet];
countedSet will now contain only elements for the duplicates (if any), and the countForObject: method will return the number of duplicates (in excess of 1) for that element.
I have an NSArray which contains some NSString objects. For example:
NSArray *objects = #[#"Stin",#"Foo",#"Ray",#"Space"];
Now I need to sort this array based on following order of Strings.
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
So the answer should be
NSArray *sorted = #[#"Foo",#"Space",#"Ray",#"Stin"];
How can I achieve this?
ANSWER:
Based on Accepted answer of dasblinkenlight, I did following and it worked to charm.
NSMutableArray *objects = #[#"Star",#"Stin",#"Foo",#"Ray",#"Space",#"John"];
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
[objects sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int index1 = [sortOrder indexOfObject:obj1];
int index2 = [sortOrder indexOfObject:obj2];
if (index1 > index2)
return NSOrderedDescending;
if (index1 < index2)
return NSOrderedAscending;
return NSOrderedSame;
}];
Create NSComparator that holds a reference to the superset array, and decides the relative order of strings by comparing the results of calling [superset indexOfObject:str] on both strings. Call sortedArrayUsingComparator: passing an instance of NSComparator to get the desired ordering.
dasblinkenlight's solution would work, but like most programming problems there are multiple ways to go about it. Here is one such alternative:
NSMutableArray *sorted = [NSMutableArray arrayWithCapacity:0];
[sortOrder enumerateObjectsUsingBlock:^(NSString *sortedString, NSUInteger idx, BOOL *stop) {
if ([objects containsObject:sortedString]) {
[sorted addObject:sortedString];
}
}];
The variable names correspond to the variable names used in the original question.
This works because the enumeration happens in order. Therefore, what takes place is, essentially, a cull of the objects that exist in both arrays in the order as specified by sortOrder.