Instead of implementing my own I was wondering if anyone knows of a histogram or bag datastructure implementation in Objective-C that I can use.
Essentially a histogram is a hashmap of lists where the lists contain values that relate to their hash entry. A good example is a histogram of supermarket items where you place each group of items dairy, meat, canned goods in their own bag. You can then very easily access each group of items according to their type.
NSCountedSet is a multiset (aka "bag") that counts distinct objects, but doesn't allow duplicates. However, based on your explanation, I don't think that's what you need, and neither is a histogram, which automatically buckets values based on a set of (usually numerical) ranges.
I believe what you really want is a multimap, which is a "key to one-or-more values" relation. The data structures framework I maintain includes CHMultiDictionary, a multimap implementation. I won't claim by any means that it's perfect or complete, but I hope it may be helpful for your problem.
It sounds to me like you simply want a dictionary of arrays. You can put NSArrays as elements of NSDictionarys, something like:
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
[dict setObject:[NSMutableArray arrayWithObjects:#"milk", #"eggs", #"cheese", nil] forKey:#"dairy"];
[dict setObject:[NSMutableArray arrayWithObjects:#"steak", #"sausages", #"mince", nil] forKey:#"meat"];
[[dict objectForKey:#"meat"] addObject:#"lamb"];
NSLog( #"Dictionary is %#", dict );
There's one in the GNU Objective-C Class library, but the docs appear to be pretty incomplete and the project's homepage must be currently having a problem -- still, if GPL software is acceptable for your project, you might want to download and check the sources.
CFIOMultimap apparently is an implementation of a multimap. However, as of the time of writing I couldn't get it to work. It returns nils all the time when I subscript.
Perhaps it can be fixed and adapted for your use.
Related
I am sorry to ask this but I have searched for hours on doing this but I really don't understand it. Please help me. I have a .plist file in my Xcode project and it's root is a Dictionary type. It contains about 50 more dictionaries. Inside the dictionary contains strings. (Dictionary(root) > Dictionary > String. I added a search field to my toolbar and linked it to my code. I am able to get what the user types but then how do I "search" after getting what the user typed? Is there a method for this and how do I link it into my .plist? Thank you so much!!!
You want to search for the user entered string in your Dictionary of Dictionaries?
You're going to have to iterate each dictionary, asking [dict objectForKey:userEntry] in each. Not sure if you want to only find first match or all matches too.
Additionally, you may want to create an abstraction of your Dictionary of Dictionaries to reduce the scale of the problem and clarify the API. In simpler terms, wrap your Dictionary of Dictionaries in a class and put a sensible (non-dictionary-based) set of methods on it. It's probably worth the effort.
To load the plist into a Dictionary, look at [Dictionary dictionaryWithContentsOfFile].
Edit: Filtering options on NSDictionary
Have you looked at the following options for filtering values in an NSDictionary:
[NSDictionary keysOfEntriesPassingTest:] (10.6 and later) or
take the [rootDictionary allValues] NSArray and use Predicates, perhaps like this.
In Foundation, if I want to convert a set to an NSArray, I can use:
-[NSSet allObjects]
-[NSOrderedSet array]
Why are these different?
Speculation, but:
Because when NSSet was created the only other major collection type was NSArray, which was (and still is, largely) the most common collection type. So a method called "allObjects" would obviously return an NSArray.
When NSOrderedSet was added much more recently, it had to deal with the existence of prior collections - primarily, NSArray and NSSet. So an "allObjects" method would be ambiguous. Ergo it has two methods, -array and -set.
And/or, the -array and -set methods return proxies to what are likely the same or similar classes used internally. So in a functional sense they're a little different - those proxies will see mutations made on the original NSOrderedSet. -allObjects on the other hand does not - it genuinely creates an independent array, since its internal storage is likely a hashtable or similar that isn't readily proxied.
While there are other differences†, .allObjects does not imply a definite ordering, and .array does; and that's exactly what you are getting.
† .array returns a live proxy of the underlying NSOrderedSet, and if the underlying ordered set changes, the proxy will change with it.
Also... The NSArray returned by 'allObjects' is a copy of the values in the set.
But the NSArray returned by 'array' is a proxy of the objects in the set.
Thus if you change the value of any object in the set, you will change the value of the object in the array. A copy of the ordered set is not being made. So the two properties have different names because they do different things.
OK this is probably a really stupid question but I can't seem to find the answer.
I know how to sort arrays using sort descriptors but what about a simple array containing only a series of numbers?
For example an array containing the following numbers:
21,3,11,58,32,76,19,45,7,92
I just need the numbers in ascending order.
I'm assuming you mean an NSArray of NSNumbers.
Fairly simple:
NSArray *unorderedNumbers; // assume exists
NSArray *sortedArray = [unorderedNumbers
sortedArrayUsingSelector:#selector(compare:)];
(See sortedArrayUsingSelector:).
This causes the objects (which happen to be numbers) to be compared using compare:.
The C standard library includes qsort
See: http://www.cplusplus.com/reference/clibrary/cstdlib/qsort/
Short:
I need to find core data objects by a key, which holds a unique immutable array (fixed length, but chosen at runtime) of arbitrary objects (for which not only element membership, but also element order determines uniqueness). NSManagedObject however forbids overriding [isEqual:]. Now what?
Long:
I have an entity (see diagram image for entity "…Link") in my Core Data model for which I have to guarantee uniqueness based on an attribute key ("tuple"). So far so good.
The entity's unique attribute however has to be an NSArray.
And to make things a bit more difficult I neither know the class type of the tuple's elements.
Nor do I know the tuple's element count. Well, actually the count is the same for every tuple (per core data context at least), but not known before the app runs.
There must only ever be one instance of my link entity with a given tuple.
And for obvious reason only ever one tuple instance with a given array of arbitrary objects.
Whereas two tuples are to be considered equal if [tuple_1 isEqual:tuple_n] returns YES. NSManagedObject forbids the overriding of [isEqual:] and [hash] though, otherwise things would be pretty much a piece of cake.
"…Tuple" objects are created together with their array of tokens (via a convenience method) and are immutable (and so is each "…Token" and its data attribute). (think of "…Tuple" as a "…Link"'s dictionary key.)
"…Tuple" implements "- (NSArray *)tokens;", which returnes a neatly ordered array of tokens, based on the "order" keys of "…TokenOrder". (Tuples are expected to contain at most 5 elements.)
I however expect to have tens of thousands (potentially even more in some edge cases) of "…Link" objects, which I have to (frequently) find based on their "tuple" attribute.
Sadly I couldn't find any article (let alone solution) for such a scenario in any literature or the web.
Any ideas?
A possible solution I've come up with so far would be:
Narrow amount of elements to compare
by tuple by adding another attribute
to "…Tuple" called "tupleHash",
which is pre-calculated on
object creation via: Snippet 1
Query with NSPredicate for objects of matching tupleHash (narrowing down the list of candidates quite a bit).
Find "…Link" featuring given tuple in narrowed candidate list by: Snippet 1
Snippet 1:
NSUInteger tupleHash = [[self class] hash];
for (id token in self.tokens) {
tupleHash ^= [token.data hash];
}
Snippet 2:
__block NSArray *tupleTokens = someTokens;
NSArray *filteredEntries = [narrowedCandidates filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock: ^(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject.tuple.tokens isEqualToArray:tupleTokens];
}]];
(Sorry, markdown appears to oppose mixing of lists with code snippets.)
Good idea of or just insane?
Thanks in advance!
I strongly suggest that you calculate a hash for your objects and store it in your database.
Your second snippet will seriously hurt performance, that's for sure.
Update:
You don't need to use the hash method of NSArray.
To calculate the hash, you can perform a SHA1 or MD5 on the array values, concatenated. There are many algorithms for hashing, these are just two.
You can create a category for NSArray, say myHash to make the code reusable.
As recommended in a comment by Joe Blow I'm just gonna go with SQLite. Core Data simply appears to be the wrong tool here.
Benefits:
Fast thanks to SQL's column indexing
No object allocation/initialization on SELECT, prior to returning the results. (which Core Data would require for attribute checks)
Easily query link tuples using JOINs.
Easy use of SQLite's JOIN, GROUP BY, ORDER BY, etc
Little to no wrapper code thanks to EGODatabase (FMDB-inspired SQLite Objective-C wrapper)
NSMutableArray *sectionTitles;
[sectionTitles addObject:due];
How do I just add unique values to an array?
See Rudolph's Answer
My old answer below is now outdated and has been for awhile. Rudoph's reference to NSOrderedSet / NSMutableOrderedSet is the correct one since these classes were added after this Q and my A.
Old Answer
As Richard said, NSMutableSet works well but only if you don't need to maintain an ordered collection. If you do need an ordered collection a simple content check is the best you can do:
if (![myMutableArray containsObject:newObject])
[myMutableArray addObject:newObject];
Update based on comment
You can wrap this in a method like -addUniqueObject: and put it in an NSMutableArray category.
More 2012-ish answer (in case someone stumbled upon this in the future):
Use NSOrderedSet and NSMutableOrderedSet.
A note about performance right from NSOrderedSet docs:
You can use ordered sets as an alternative to arrays when the order of elements is important and performance in testing whether an object is contained in the set is a consideration— testing for membership of an array is slower than testing for membership of a set.
Use NSMutableSet, it is best for these situations
iOS Reference
Mac OSX Reference