I have an app with a relatively large SQLite database, containing around 15,000 rows. At the start of the app, I run a SELECT * FROM TABLE query and populate a NSMutableArray *data containing elements of a class ProverbRow. Each ProverbRow object corresponds to one row of data from the database and contains NSStrings and NSIntegers corresponding to each cell of the database. So, effectively the NSMutableArray *data object is a copy of the SQLite database.
Now, to the question...
I want to add a search functionality in the app like this example :
When the user types in "abc" and taps search, I want to get all the elements of data where the NSString *proverb inside the ProverbRow object contains the string "abc" as the substring. So, the strings like "abcde", "qqqabcqqq", etc. should be obtained.
Currently, to accomplish this, I am firing a query like this : SELECT * FROM PROVERB WHERE PRONUNCIATION LIKE abc. The objects which I get back from the query are stored in another NSArray for further use.
Now, if there are only a small number of objects getting returned, then this query completes fast enough, but with larger number of rows, it takes a lot of time.
I was wondering whether there is a quicker way to accomplish this apart from firing the query. Is it possible to use the already populated data object and run it through a loop and equating the substring or something like that? My main concern is to reduce the time search takes.
Thanks!
15 k is tiny. Just do a linear scan of the in-memory objects.
The following test finds the xyz at the end of the alphabet 15000 times. Repeating this process 100 times took 4.7 s on my iPhone 5. That's 47 ms for a full scan that returns every element.
NSMutableArray * data = [[NSMutableArray alloc] initWithCapacity:15000];
for (int i = 0; i < 15000; ++i)
[data addObject:#[#"abcdefghijklmnopqrstuvwxyz", #123]];
NSLog(#"Starting test");
int count = 0;
for (int i = 0; i < 100; ++i) {
NSIndexSet * s = [data indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSString * s = obj[0];
return [s rangeOfString:#"xyz"].location != NSNotFound;
}];
count += s.count;
}
NSLog(#"Finished test: %d", count); // Outputs 1500000
Let me reiterate: 15 k is tiny.
Related
I have two very large NSMutableArray of strings containing more than 40k records each. I have to take each element from one array and sort that string into another array then make a new array which conatins only those records that are in both array. I have implemented the following code which take too much time as well as a lot of memory space also (crash in device). Are there any ways to solve this problem in a more efficient manner.
// _perArray and listArray contains more then 30K records each
for(NSString *gak in _perArray){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF LIKE[c] %#",gak];
NSArray *results = [listArray filteredArrayUsingPredicate:predicate];
if(results.count>0){
[_resultArray addObject:results[0]];
}
}
Use binary search
index sort one array (the one with less records)
this will enable the usage of binary searching
sort the lesser array just to need less memory for index array
loop through the second array
for each record binary search in the first array
if found add record to output array
do not forget to preallocate the output array to avoid reallocation slowdowns
What it means:
Let N,M be the array sizes where N<=M
naive approach is O(N.M)
this approach (depending on used sort) leads to O(N.log(N).log(M))
Sort both arrays and use single pass incremental search
the complexity will lead to something like O((N.log(N))+(M.log(M))+M)
which in therms of complexity turns to O(M.log(M))
So:
index sort booth arrays
loop through M
increment index for array with lesser record
if match found add it to output array
To be more specific bullet 2 will be something like this (if arrays are sorted ascending):
// variables
string m[M],n[N],o[N]; // your arrays any string type with overloaded <,== operators
int M,N,O; // arrays sizes
int ixm[M],ixn[N]; // indexes for index sort
int i,j;
// bullet 2
for (i=0,j=0,O=0;;)
{
if (m[ixm[i]]==n[ixn[j]]) { o[O]=m[ixm[i]]; O++; }
if (m[ixm[i]]< n[ixn[j]]) { if (i<M) i++; else { if (j<N) j++; else break; }}
else { if (j<N) j++; else { if (i<M) i++; else break; }}
}
If you encode the string comparisons right you can do booth if conditions with single comparison
[notes]
if you do not want to use any of these approaches then there is also another way
you can add flag to one array telling you if it is already used
if it is skip the use of it during your comparisons
that will speed up your naive approach about 2 times
from M.N string comparisons you will need to do just M.N/2
If you have too big data chunks to fit in memory
then segmentate both arrays to some size fit to memory/Cache/...
and first index sort all segments
then do one of the above approach on all segments combinations
the only thing you need to add is checking if O[] does not already contain added string
if you arrays does not have multiples of the same string then this is not the case
otherwise keep O[] sorted or index sorted
and check by binary search ...
this segmentation will be speeded up with the used flag significantly
Always balance the performance issues with the frequency this code path is called. Going the database route might introduce a whole new set of issues to deal with while simply performing the sort in the background, and cutting down the size of the array first might be good enough.
Remove the duplicates first using NSMutableSet
Add all the objects, NSString in this case, to an NSMutableSet. This will eliminate the duplicates. Then sort the remaining objects.
NSArray *array1;
NSArray *array2;
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithArray:array1];
[mutableSet addObjectsFromArray:array2];
NSSortDescriptor *sortDescriptor = nil; // You'll need to create a sort descriptor.
NSArray *result = [mutableSet sortedArrayUsingDescriptors:#[sortDescriptor]];
// Alternative
NSArray *result = [[mutableSet allObjects] sortedArrayUsingSelector:#selector(compare:)];
I wrote a quick Obj-C test you can try at the command-line.
Run it in the background and return when finished.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// Perform the sorting
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Tell the main thread I'm done.
});
});
I am developing an iPad application and for this application I have one function as below :-
-(void)testcurrentest:(NSMutableDictionary *)keydictionary{
NSArray *allKeys = [keydictionary allKeys];
if ([allKeys count] > 0) {
for(int i = 0;i< allKeys.count;i++){
[_currenies removeAllObjects];
NSString *product = [NSString stringWithFormat:#"%#", [keydictionary objectForKey:allKeys[i]]];
int kl = [productPriceSeasonCode intValue];
for(int i =0;i<kl;i++){
[_currenies addObject:#"0"];
}
NSLog(#"................%#",_currenies);
[_currencydictionary1 setObject:_currenies forKey:allKeys[i]];
NSLog(#"full dictionary...%#",_currencydictionary1);
}
}
}
Here, NSLog print the currencies array based on the kl integer values but when I'm trying to set the NSMutableDictionary the currencies but mutable array always show the latest array values.
You are using the same array for all values, they should be unique objects if you don't want change of one value to affect the other values. Initialise _currenies on every loop step or use its deep copy when preparing a new object.
A bit of code:
[_currenies removeAllObjects]; // < The same array you've added to dict on previous loop steps
Creating a new array at each loop step would create a unique object for all key-value pair:
_currenies = [NSMutableArray array]; // < Note it is not retained, apply memory management depending on your project configuration
Your code is a garbled mess. As others have pointed out, you are using the same loop index, i, in 2 nested loops, making it very hard to tell your intent. Don't do that, ever. It's horrible programming style.
You are also creating a string "product" that you never use, and fetching the same integer value of productPriceSeasonCode on every pass through the outer loop. I suspect you meant to fetch a value that varies with each entry in your keydictionary.
Then, you have an array, _currenies, which you empty on each pass through your outer loop. You then add a number of "0" strings to it, set a key/value pair in your _currencydictionary1 dictionary to the contents of that array, and then repeat. Since you re-use your _currenies array each time, every key/value pair you create in your _currencydictionary1 dictionary points to the exact same array, which you keep changing. At the last iteration of your outer loop, all the entries in your _currencydictionary1 will point to your _currenies array, which will contain the last set of contents you put there.
Create a new array for each pass through your outer array, and add that newly created array to your _currencydictionary1. You want a unique array in each key/value pair of your _currencydictionary1.
In short, NSMutableDictionary is working just fine. It's your code that isn't working properly.
Not an answer but comments don't have formatting.
The question should provide more information on the input and desired output.
First simplify your code and it should be easier to find the error:
-(void)testcurrentest:(NSMutableDictionary *)keydictionary{
NSArray *allKeys = [keydictionary allKeys];
for(NSString *key in allKeys) {
[_currenies removeAllObjects];
int kl = [productPriceSeasonCode intValue];
for(int i =0; i<kl; i++){
[_currenies addObject:#"0"];
}
NSLog(#"................%#",_currenies);
_currencydictionary1[key] = _currenies;
NSLog(#"full dictionary...%#",_currencydictionary1);
}
}
Note: product was never used.
This question already has answers here:
Getting a random object from NSArray without duplication
(3 answers)
Closed 7 years ago.
I have an array of random properties I would like to assign to equipment within the game I'm developing.
The code that I use below is returning an NSArray. I'm interested if there's way to get item indices from that array without getting duplicate values. The obvious solution is to create a mutable array with the returned array, do random, remove item that was returned and loop until the number of items is received.
But is there a different way of getting X random items from NSArray without getting duplicates?
//get possible enchantments
NSPredicate *p = [NSPredicate predicateWithFormat:#"type = %i AND grade >= %i", kEnchantmentArmor,armor.grade];
NSArray* possibleEnchantments = [[EquipmentGenerator allEnchantmentDictionary] objectForKey:#"enchantments"];
//get only applicable enchantments
NSArray *validEnchantments = [possibleEnchantments filteredArrayUsingPredicate:p];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:validEnchantments];
NSDictionary* enchantment = nil;
if(mutableArray.count>0)
{
//got enchantments, assign number and intensity based on grade
for (int i = 0; i<3;i++)
{
enchantment = mutableArray[arc4random()%mutableArray.count];
[mutableArray removeObject:enchantment];
//create enchantment from dictionary and assign to item.
}
}
You can shuffle the array using one of the following techniques:
What's the Best Way to Shuffle an NSMutableArray?
Non repeating random numbers
Then, take the first X elements from the array.
Many years ago, I was working on card game and I realized that shuffling the deck was an inefficient way to get random cards. What I would do in your shoes is pick a random element, and then replace it with the element at the end of the array, like so:
#interface NSMutableArray (pickAndShrink)
- (id) pullElementFromIndex:(int) index // pass in your random value here
{
id pickedItem = [self elementAtIndex:index];
[self replaceObjectAtIndex:index withObject:[self lastObject]];
[self removeLastObject];
return pickedItem;
}
#end
The array will shrink by one every time you pull an element this way.
You could use a random number generator to pick a starting index, and then pick the subsequent indices based on some kind of math function. You would still need to loop depending on how many properties you want.
Eg:
-(NSMutableArray*)getRandomPropertiesFromArray:(NSArray*)myArray
{
int lengthOfMyArray = myArray.count;
int startingIndex = arc4random()%lengthOfMyArray;
NSMutableArray *finalArray = [[NSMutableArray alloc]init]autorelease];
for(int i=0; i<numberOfPropertiesRequired; i++)
{
int index = [self computeIndex:i usingStartingIndex:startingIndex origninalArray:myArray];
[finalArray addObject:[myArray objectAtIndex:index]];
}
return finalArray;
}
-(int)computeIndex:(int)index usingStartingIndex:(int)startingIndex
{
//You write your custom function here. This is just an example.
//You will have to write some code to make use you don't pick an Index greater than the length of your array.
int computedIndex = startingIndex + index*2;
return startingIndex;
}
EDIT: Even your computeIndex function could use randomness in picking the subsequent indices. Since you have a startingIndex, and another index, you could use that to offset your function so that you never pick a duplicate.
EDIT: If your array is very large, and the subset you need to pick is small, then rather than shuffle the entire array (maybe more expensive), you could use this method to pick the number of items you need. But if your array is small, or if the number of items you need to pick are almost the size of the array, then the godel9's solution is better.
You can use a mutable array and then remove them as the are selected, use something like random()%array.count to get a random index. If you don't want to modify the array then copy it with [array mutableCopy].
Currently I'm trying to find a compact way to average a matrix. The obvious solution is to sum the matrix, then divide by the number of elements. I have, however, come across a method on the apple developer website that claims this can be done in a simpler way using valueForKeyPath. This is linked here:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html
Here is the example I'm currently working on to try and get it to work:
-(void)arrayAverager
{
NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:25];
[myArray addObject:[NSNumber numberWithInteger:myValue]];
NSNumber *averageValue = [myArray valueForKeyPath:#"#avg.self"];
NSLog(#"avg = %#", averageValue);
}
The problem is: instead of averaging the array it merely prints out the elements in the array 1 by 1.
UPDATE
-(void) pixelAverager
{
//Put x-coordinate value of all wanted pixels into an array
NSMutableArray *xCoordinateArray = [NSMutableArray arrayWithCapacity:25];
[xCoordinateArray addObject:[NSNumber numberWithInteger:xCoordinate]];
NSLog(#"avg = %#", [xCoordinateArray valueForKeyPath:#"#avg.intValue"]);
}
You need to use #avg.floatValue (or #avg.doubleValue, or what have you). The #avg operator will average the property of the objects in the array specified by the name after the dot. The documentation is confusing on this point, but that is what:
to get the values specified by the property specified by the key path
to the right of the operator
Is saying. Since you have a collection of NSNumber objects, you use one of the *value accessors, e.g. floatValue to get the value of each object. As an example:
#include <Foundation/Foundation.h>
int main(void) {
NSMutableArray *ma = [NSMutableArray array];
[ma addObject:[NSNumber numberWithFloat:1.0]];
[ma addObject:[NSNumber numberWithFloat:2.0]];
[ma addObject:[NSNumber numberWithFloat:3.0]];
NSLog(#"avg = %#", [ma valueForKeyPath:#"#avg.floatValue"]);
return 0;
}
Compiling and running this code returns:
$ clang avg.m -framework Foundation -o avg
steve:~/code/tmp
$ ./avg
2013-01-18 12:33:15.500 avg[32190:707] avg = 2
steve:~/code/tmp
The nice thing about this approach is that this work for any collection, homogenous or otherwise, as long as all objects respond to the specified method, #avg will work.
EDIT
As pointed in the comments, the OP's problem is that he is averaging a collection with one element, and thus it appears to simply print the contents of the collection. For a collection of NSNumber objects, #avg.self works just fine.
No, you can't do like this. The object Transaction is a Modal Class. This class is having three properties, namely
payee
amount
date
Each row in this image represents one Transaction modal object.
transactions is an array which is holding all these rows (Transaction Modal Objects).
In these transactions array, they are trying to calculate the Transaction Modal amount field average using the operator #avg. So, its like
NSNumber *transactionAverage = [transactions valueForKeyPath:#"#avg.amount"];
your array doesn't have the key self. So that's the problem
This may be out of the scope of what Objective C can do.
It involves doing something to a named variable (e.g. 15 different operations), and then doing those 15 operations on a similarly named variable, and so on for let's say 10 variables.
Suppose I have a set of 10 NSMutableArray;e.g.
NSMutableArray *a =[[NSMutableArray alloc]init];
...
NSMutableArray *j =[[NSMutableArray alloc]init];
and I do 15 operation on them to get 15 different variables (also arrays or more accurately NSMutableArray's). Let's say the first thing I do is to make a computation the is the product of a thru j:
for (int i=0;i<[a count]; i++)
aProduct = /* some operation upon NSMutableArray a*/...;
...
for (int i=0;i<[j count]; i++)
jProduct = /* some operation upon NSMutableArray j*/...;
Let's say the 15th thing I do is to make a computation the is the selection of a thru j:
for (int i=0;i<[a count]; i++)
aSelection = /* some operation upon NSMutableArray a*/...;
...
for (int i=0;i<[j count]; i++)
jSelection = /* some operation upon NSMutableArray j*/...;
Right now I'm using switch / case for the coding, which involves a lot of thinking, initialization, and even caught that I made a typo in one "case" statement (leaving out a break) and when I copied the code, that mistake propagated down.
So is there a way to avoid having 15 sets of "switch / case" code, and doing it instead "dynamically" (if such a thing exists in Xcode and iOS) and avoiding creating 10 times 15 variables?
Or that I create and declare the 150 variables, but do so in some array, perhaps by using the addressing (&a thru &j) of the variable?
This is getting very complex, very quickly, and there are times when some of the 10 arrays have many elements, and other of the same arrays are null (e.g. a may have 20 elements, and j has null elements)
PS: (Postscript at 6:56pm NYC time 19Mar2012) I was actually hoping that I could use some variable name and then prefix or suffix it with the letters a thru j; eg:
ProductA = [[....;
SelectorA=....;
and have the subroutine just take a letter from A to J via enum to translate that to 0 to 9.
I'm not rejecting the solution proposed earlier by Peter Cetinski; I just haven't tried to implement it yet.
If you need to maintain the computed values for each array, then try using array of arrays
NSMutableArray *arrOfArrays = [[NSMutableArray alloc] init];
[arrOfArrays addObject:a];
.....
[arrOfArrays addObject:j];
for ( NSMutableArray *arr in arrOfArrays ) {
for (int i=0;i<[arr count]; i++) {
aProduct = /* some operation upon NSMutableArray arr*/...;
.....
aSelection = /* some operation upon NSMutableArray arr*/...;
//do something with your computed values
}
}