Sorted Set in iOS - swift

I have a Set of objects, let's say Fruits:
let uniqueFruits = Set(Fruit("Apple"), Fruit("Banana"), Fruit("Orange"))
And want to sort them based on a certain atteribute. In this case "size".
I cannot find a way to do this based on the documentation from Apple:
https://developer.apple.com/reference/foundation/nsmutableset
How can I sort a Set by a certain attribute?

You have to convert the Set to an Array.
The reason for this is the following definition:
"Sets are different in the sense that order does not matter and these
will be used in cases where order does not matter."
Whereas a set:
"... stores distinct values of the same type in a collection with no
defined ordering."
See for further information:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html
In this case you will have a list of distinct values (and I consider your decision to use a NSSet as valid argument) you will have to transform your set to an array, you should not run into trouble, as your set already seems to take care of that your objects are of the same type (e.g. "Fruit").
So in this case, we will have
Define Sort Criteria
Sort the Array
I have attached a sample for both Objective-C and Swift, in case you need the one way or the other:
Objective-C Code
NSMutableSet<Fruit> *uniqueFruits = [NSMutableSet new];
[uniqueFruits addObject:[[Fruit alloc] initWithName:#"Apple"]];
[uniqueFruits addObject:[[Fruit alloc] initWithName:#"Banana"]];
[uniqueFruits addObject:[[Fruit alloc] initWithName:#"Orange"]];
// 1 Define Sort Criteria
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"size" ascending:YES]; // Key is the NSString of a certain selector. If it is an attribute of another class reference. Simply use "reference.property".
// 2 Sort the Array
NSArray<Fruit> *sortedArray = [self.uniqueFruits sortedArrayUsingDescriptors:#[descriptor]];
Swift 3 Code
let uniqueFruits = Set<Fruit>(Fruit("Apple"), Fruit("Banana"), Fruit("Orange"))
// 1 & 2 Define Sort Criteria and sort the array, using a trailing closure that sorts on a field/particular property you specify
// Be aware: result is an array
let sortedArray = uniqueFruits.sort({ $0.size < $1.size })

There is NSOrderedSet and its mutable sibling NSMutableOrderedSet which are exactly that: a set that maintains an order. The mutable ordered also has various methods to sort the set. In Swift, it's a bit awkward to use since you can't create a NSMutableOrderedSet<Fruit> and can only use it with objects anyway.

Related

Sorting array with three different sorting options

I have data collection(NSArray) of NSDictionary. Now in the app i need to show that data in three different sorting order. Now I do not want to make 3 different array (one for each sorting order with complete data) because it will consume memory nor I want to re-sort main array each time when user change sorting option.
so now my problem is, I want a mechanism that I can only get indexes of main array in 3 different array(or any other datatype) in 3 different sorting order. Should I have to do this manually or is there a build in method for geting sorted index array from main array?
What you're asking is impossible. You want to have your items sorted in 3 different orders without saving them into different arrays AND without sorting them every time the sorting method changes.
You can't get it for free. You can pay either with memory or with performance.
I'd go for memory, since it's not as costly as you think. Only the pointers to the NSDictionaries are kept in your array, so it shouldn't take too much memory.
NSArray takes only objects as its elements. And objects pointers are stored in the array. So I guess, the option you are talking about, saving indexes in another array is something similar like saving pointers in sorting order. So, you have create 3 different arrays with the pointers to objects in a particular order, it will be the same, not taking more memory. When you copy the sorted objects in new arrays, the objects will not be allocated again, only their pointers will be stored. However you have to do the sorting in right way.
If you want to keep your data in a single array, then you can use an NSMutableArray and (re-)sort it whenever the sort criteria changes.
Notice however that this means that if your data array is part of your model, and your sorting order is a presentation (UI) issue, then you are effectively mixing model, view and controller functionality in one object. Bad ( :-) ) but not the first time it has happened in history.
Here is an example using sort descriptors directly on keys of your dictionaries:
NSMutableArray *data = // ... your data as a mutable array
NSString* sortKey = // ... one of the 3 dictionary keys that you wish to sort by
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:sortKey ascending:YES];
[data sortUsingDescriptors:#[sd]];
The data array is now sorted without using an extra array variable.
Step1 : Declare Enum Definition before import statement
enum COMP_TYPE {
LOCALIZE_COMPARE = 1,
CASE_INSENSITIVE = 2,
LOCALIZE_CASE_INSENSITIVE = 3
};
Step2 : Define following function in your .m file
-(NSArray *)sortArray:(NSArray *)arr WithType:(int)type andKey:(NSString *)key andOrder:(BOOL)order
{
SEL _compareSelector;
switch (type) {
case LOCALIZE_COMPARE:
_compareSelector = #selector(localizedCompare:);
break;
case CASE_INSENSITIVE:
_compareSelector = #selector(caseInsensitiveCompare:);
break;
case LOCALIZE_CASE_INSENSITIVE:
_compareSelector = #selector(localizedCaseInsensitiveCompare:);
break;
default:
break;
}
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:key ascending:order selector:_compareSelector];
return [arr sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
}
Step3 : Declare your Array of Dictionary
NSArray *myArr = [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:#"stack", #"name",
#"goa", #"name1",nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"stack", #"name",
#"goa", #"name1",nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"objective", #"name",
#"c++", #"name1",nil], nil];
Step4 : call function with diferent Parameter
NSLog(#"%#",[self sortArray:myArr WithType:LOCALIZE_COMPARE andKey:#"name" andOrder:YES]);
NSLog(#"%#",[self sortArray:myArr WithType:CASE_INSENSITIVE andKey:#"name" andOrder:YES]);
NSLog(#"%#",[self sortArray:myArr WithType:LOCALIZE_CASE_INSENSITIVE andKey:#"name" andOrder:YES]);
It is not possible to sort the same array for different orders.
You need to save the array each time before sorting.

Getting length of an array inside a Dictionary Key

I'm sure this is a very obvious question but I'm not getting anywhere with it and I've been trying for half an hour or so now.
I have an NSMutableDictionary which has keys & values, obviously. Each key stores an array of objects. What I need to do is find a specific array in a key and get the list of the array. The catch is that I don't know the value of the key, I just know it's index. (EG: I know I need to find the array in the 2nd key).
I am almost certain this is a very easy & trivial thing to do but it's escaping me, I've only been doing Obj-C for a short while so not entirely at home with it yet!
Thanks,
Jack.
Use allKeys: to access the keys of your dictionary.
- (NSArray *)allKeys
Use as below .
NSArray* dictAllKeys = [dict allKeys];
if([dictAllKeys count] > 2)
{
NSArray* myArrayInDict = [dict objectForKey:[dictAllKeys objectAtIndex:1]];
// get the length of array in dict at 2nd key
int length = [myArrayInDict count];
}
The order will probably change if another key/value pair is added, it is a NSMutableDictionary. It is best not to rely on the order of a NSDictionary or NSSet.
Suggestion: Either use another container that does provide ordering such as NSMutableArray or find the item using the dictionary's value arrays, perhaps with NSPredicate.

Find index of an NSArray by passing value

Is it possible in an NSArray to find out if a given value exists or not in the array (without searching it using a for loop)? Any default random method. I went through the documentation, but didn't find much relevant.
Please also tell me about valueForKey method (I was unable to get that from doc).
The containsObject: method will usually give you what you're asking - while its name sounds like you are querying for a specific instance (i.e. two object with the same semantic value would not match) it actually invokes isEqual: on the objects so it is testing by value.
If you want the index of the item, as your title suggests, use indexOfObject:, it also invokes isEqual: to locate the match.
valueForKey: is for when you have an array of dictionaries; it looks up the key in each dictionary and returns and array of the results.
I believe you want to use the indexOfObject method. From the documentation:
indexOfObject:
Returns the lowest index whose
corresponding array value is equal to
a given object.
- (NSUInteger)indexOfObject:(id)anObject
Parameters
anObject
An object.
Return Value
The lowest index whose corresponding
array value is equal to anObject. If
none of the objects in the array is
equal to anObject, returns NSNotFound.
Discussion
Objects are considered equal if
isEqual: returns YES.
Important: If anObject is nil an
exception is raised.
You can use :
NSInteger idx = [myArray indexOfObject:obj];
to find index of object.
And to check if object is there or not in array you may use :
- (BOOL)containsObject:(id)anObject
Use NSPredicate to filter an NSArray based on NSDictionary
array = [dictionary filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(key == %#)", value]];
if([array count]>0)
{
value exists;
}
Combine objectAtIndex and indexOfObject like this:
[tmpArray objectAtIndex:[tmpArray indexOfObject:yourObject]];

Objective-C, How can I produce an array / list of strings and count for each?

My aim is to produce an array, which I can use to add section headers for a UITableView. I think the easiest way to do this, is to produce a sections array.
I want to create section headers for dates, where I'll have several or no rows for each.
So in my populate data array function, I want to populate a display array. So record 1, look for the first date in my display array, create a new array item if it doesn't exist, if it does exist add 1 to the count.
So I should end up with something like this.
arrDisplay(0).description = 1/June/2001; arrDisplay(0).value = 3;
arrDisplay(1).description = 2/June/2001; arrDisplay(1).value = 0;
arrDisplay(2).description = 3/June/2001; arrDisplay(2).value = 1;
arrDisplay(3).description = 5/June/2001; arrDisplay(3).value = 6;
My question is how do I create and use such an array with values, where I can add new elements of add to the count of existing elements and search for existing elements ?
I think, if i understand you, an NSMutableDictionary would work. (as NR4TR said) but, i think the object would be the description and the key would be the count. you could check for the key and get the count in the same gesture. if the return value of objectForKey is nil, it doesn't exist.
NSMutableDictionary *tableDictionary = [[NSMutableDictionary alloc] init];
NSString *displayKey = #"1/June/2001";
NSNumber *displayCount = [tableDictionary objectForKey:displayKey];
if (displayCount != nil) {
NSNumber *incrementedCount = [[NSNumber alloc] initWithInteger:[displayCount integerValue] + 1];
[tableDictionary removeObjectForKey:displayKey];
[tableDictionary setValue:incrementedCount
forKey:displayKey];
[incrementedCount release];
}
else {
NSNumber *initialCount = [[NSNumber alloc] initWithInteger:1];
[tableDictionary setValue:initialCount
forKey:displayKey];
[initialCount release];
}
EDIT: Hopefully this isn't pedantic, but I think a couple pointers will help.
Dictionaries, Sets, and Arrays all hold objects for retrieval. The manner of holding and retrieval desired drives the decision. I think of it based on the question 'what is the nature of the information that I have when I need an object being held?'
NSDictionary and NSMutableDictionary
Hold n objects per key. (I think...I haven't had to test a limit, but i know you can get an NSSet back as a value.)
KEY is more important than INDEX. I don't think of dictionaries as ordered. they know something and you need to ask the correct question.
NSArray and NSMutableArray
hold n objects in order.
INDEX is most important bit of information. (you can ask for the index of an object but, even here, the index is the important part)
you will typically drive table views with an array because the ordered nature of the array fits.
NSSet, NSMutableSet, and NSCountedSet
A collection of objects without order.
You can change any of these into the other with something like [nsset setFromArray:myArray];
and all of these things can hold the other as objects. I think an array as your top level is the correct thinking, but beyond that, it becomes an issue of implementation
Try array of dictionaries. Each dictionary contains two objects - section title and array of section rows.
If you want to have a description AND a rowcount then you can either create a class with those two properties and generate an NSArray of objects with that class or instead of all that you can just use an NSDictionary to store key/value lookups.
I think NSCountedSet is closest to what you want. It doesn't have an intrinsic order, but you can get an array out of it by providing a sort order.

NSDictionary split into two arrays (objects and keys) and then sorted both by the objects array (or a similar solution)

I have an NSDictionary. It has keys and objects.
For the purposes of simplicity the keys are Question numbers and the objects are calculated Answer scores.
Now how I did it before was that I set the answer score as the keys and the question numbers as the objects. This way I could get an array of allKeys from the dictionary, sort it and then do something similar to:
for(NSString *string in tempArray){
NSLog(#"%#",[dictionary objectForKey:string]);
}
The (stupid - on my part) problem that I have now encountered however is that (obviously... duuhhh) the keys need to unique, and therefore when the calculated answer scores are the same, only one answer gets output!
I need a solution to this. In PHP you can multisort arrays. I was wondering if there was some similar solution in objective-c or indeed if someone had a better answer?
Any help here would be much appreciated.
Thank you.
Do you know about the allKeys, allValues and allKeysForObject method of the NSDictionary do you ?
One solution is to store the answer scores using an array of dictionaries containing only two key-value pairs. One key is the question number (or however your questions are tagged, i.e. “Q1.1”), while the other key is the actual answer score. For example:
static NSString * const QuestionKey = #"questionNumber";
static NSString * const AnswerScoreKey = #"answerScore";
NSMutableArray *allAnswers = [NSMutableArray array];
for (int i = 0; i < 20; i++)
{
// fill allAnswers array with random data
NSDictionary *answer = [NSDictionary dictionaryWithObjectsForKeys:
[NSString stringWithFormat:#"Q%d", i], QuestionKey,
[NSNumber numberWithInt:rand()], AnswerScoreKey,
nil];
[allAnswers addObject:answer];
}
// sort the allAnswers array based on score, highest first
NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:AnswerScoreKey ascending:NO];
[allAnswers sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
for (NSDictionary *answer in allAnswers)
{
NSLog(#"Question: %#, AnswerScore: %#", [answer objectForKey:QuestionKey], [answer objectForKey:AnswerScoreKey];
}
Disclaimer:
Untested and uncompiled code. Theory only.
I am not sure what you are actually trying to achieve. Do you want to sort an array of dictionary objects based on the value of the dictionary? If that is what you want, you can define your own comparator function and can define any custom sorting behavior. Please check sortedArrayUsingSelector: method of NSArray.
Edit : I am away from Mac currently but this previous question has a number of example code that can solve your problem. Rather than using object, you can use NSDictionary, though personally I would like to use another Question object instead of dictionary in this case. This question class will contain two value, id and score and then you need to sort the array of questions based on score just like the persons are sorted based on birthday.