How to loop through and add duplicates in an Array - objective-c

I have a Dictionary with an Orders array with amount and orders in it Orders = ("3 White Shirts", "8 White Shirts", "4 blue shorts")
How would I loop through it and add the amount of duplicate orders so that the result string or array would be
Orders = ("11 White Shirts", "4 blue shorts") or myString ="11 White Shirts, 4 blue shorts"
I'm thinking some sort of substring to check if the products are the same but not sure how to capture the correct quantity to add from the duplicate order
Many thanks

Ok here is a way to do this (the shortest one I could think of):
// Assuming that 'orders' is the array in your example
NSMutableDictionary *orderDict = [[NSMutableDictionary alloc] init];
for (NSString *order in orders)
{
// Separate the string into components
NSMutableArray *components = [[order componentsSeparatedByString:#" "] mutableCopy];
// Quantity is always the first component
uint quantity = [[components objectAtIndex:0] intValue];
[components removeObjectAtIndex:0];
// The rest of them (rejoined by a space is the actual product)
NSString *item = [components componentsJoinedByString:#" "];
// If I haven't got the order then add it to the dict
// else get the old value, add the new one and put it back to dict
if (![orderDict valueForKey:item])
[orderDict setValue:[NSNumber numberWithInt:quantity] forKey:item];
else{
uint oldQuantity = [[orderDict valueForKey:item] intValue];
[orderDict setValue:[NSNumber numberWithInt:(oldQuantity+quantity)] forKey:item];
}
}
This would give you a dict like this:
{
"White Shirts" = 11;
"blue shorts" = 4;
}
So you could iterate over the keys and produce an array of strings like this:
NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:0];
for (NSString *key in [orderDict allKeys])
{
[results addObject:[NSString stringWithFormat:#"%# %#", [orderDict valueForKey:key], key]];
}
Which finally will give you:
(
"11 White Shirts",
"4 blue shorts"
)
PS. Don't forget to release if you don't use ARC !

You need to parse the strings to extract the two pieces of information: order quantity, which is a numeric value, and item identification, which may remain a string.
Use a NSMutableDictionary mapping the item identification to a numeric value representing the current order quantity, else retrieve the old total and add to it the current order, then update the dictionary.
At the end iterate the dictionary and transform each key-value pair back into a string.

Since it looks like your array contains string objects, I would do something like this:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *ordersAsStrings = [NSArray arrayWithObjects:#"7 white shirts", #"4 blue jeans", #"3 white shirts", #"4 blue jeans", nil];
NSMutableDictionary *combinedQuantities = [NSMutableDictionary new];
NSMutableArray *combinedOrdersAsStrings = [NSMutableArray new];
// take each string and break it up into the quantity and the item
for (NSString *orderAsString in ordersAsStrings) {
NSInteger scannedQuantity = 0;
NSString *scannedItem = nil;
NSScanner *scanner = [NSScanner scannerWithString:orderAsString];
[scanner scanInteger:&scannedQuantity];
[scanner scanCharactersFromSet:[[NSCharacterSet illegalCharacterSet] invertedSet] intoString:&scannedItem];
// if the item is already in combinedOrders
if ([combinedQuantities.allKeys containsObject:scannedItem] == YES) {
// update quantity
NSNumber *existingQuantity = [combinedQuantities objectForKey:scannedItem];
NSInteger combinedQuantity = existingQuantity.integerValue + existingQuantity.integerValue;
[combinedQuantities setObject:[NSNumber numberWithInteger:combinedQuantity] forKey:scannedItem];
} else {
// otherwise add item
NSNumber *quantity = [NSNumber numberWithInteger:scannedQuantity];
[combinedQuantities setObject:quantity forKey:scannedItem];
}
}
// change combined quantities back into strings
for (NSString *key in combinedQuantities.allKeys) {
NSNumber *quantity = [combinedQuantities objectForKey:key];
NSString *orderAsString = [NSString stringWithFormat:#"%ld %#", quantity.integerValue, key];
[combinedOrdersAsStrings addObject:orderAsString];
}
NSLog(#"Combined Orders: %#", combinedOrdersAsStrings);
}
return 0;
}

Related

NSString to NSArray and editing every object

I have an NSString filled with objects seperated by a comma
NSString *string = #"1,2,3,4";
I need to seperate those numbers and store then into an array while editing them, the result should be
element 0 = 0:1,
element 1 = 1:2,
element 2 = 2:3,
element 3 = 3:4.
How can i add those to my objects in the string ??
Thanks.
P.S : EDIT
I already did that :
NSString *string = #"1,2,3,4";
NSArray *array = [string componentsSeparatedByString:#","];
[array objectAtIndex:0];//1
[array objectAtIndex:1];//2
[array objectAtIndex:2];//3
[array objectAtIndex:3];//4
I need the result to be :
[array objectAtIndex:0];//0:1
[array objectAtIndex:1];//1:2
[array objectAtIndex:2];//2:3
[array objectAtIndex:3];//3:4
In lieu of a built in map function (yey for Swift) you would have to iterate over the array and construct a new array containing the desired strings:
NSString *string = #"1,2,3,4";
NSArray *array = [string componentsSeparatedByString:#","];
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[newArray addObject:[NSString stringWithFormat:#"%lu:%#", (unsigned long)idx, obj]];
}];
The first thing you need to do is separate the string into an array of component parts - NSString has a handy method for that : '-componentsSeparatedByString'. Code should be something like this :
NSArray *components = [string componentsSeparatedByString:#","];
So that gives you 4 NSString objects in your array. You could then iterate through them to make compound objects in your array, though you arent exactly clear how or why you need those. Maybe something like this :
NSMutableArray *resultItems = [NSMutableArray array];
for (NSString *item in components)
{
NSString *newItem = [NSString stringWithFormat:#"%#: ... create your new item", item];
[resultItems addObject:newItem];
}
How about this?
NSString *string = #"1,2,3,4";
NSArray *myOldarray = [string componentsSeparatedByString:#","];
NSMutableArray *myNewArray = [[NSMutableArray alloc] init];
for (int i=0;i<myOldarray.count;i++) {
[myNewArray addObject:[NSString stringWithFormat:#"%#:%d", [myOldarray objectAtIndex:i], ([[myOldarray objectAtIndex:i] intValue]+1)]];
}
// now you have myNewArray what you want.
This is with consideration that in array you want number:number+1

Objective-C: How to find the most common string in an array?

I have an array of strings from an online database that I trying to determine the most commonly used word. The values inside the arrays will vary but I want to check the most common words of whatever collection or words I'm using. If theoretically I had an array of the following...
NSArray *stringArray = [NSArray arrayWithObjects:#"Duck", #"Duck", #"Duck", #"Duck", #"Goose"];
How do I iterate through this array to determine the most common string, which would obviously be "Duck"?
Simplest way is probably NSCountedSet:
NSCountedSet* stringSet = [[NSCountedSet alloc] initWithArray:strings];
NSString* mostCommon = nil;
NSUInteger highestCount = 0;
for(NSString* string in stringSet) {
NSUInteger count = [stringSet countForObject:string];
if(count > highestCount) {
highestCount = count;
mostCommon = string;
}
}
You can use the word as a key into a dictionary.
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
}
Now iterate through words and find the key with the highest value.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
for (NSString *key in [words allKeys]) {
if ([words[key] compare:curMax] == NSOrderedDescending) {
mostCommon = key;
curMax = word[key];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
EDIT: Rather than looping through the array once then looping separately through the sorted dictionary, I think we can do better and do it all in a single loop.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
if ([words[word] compare:curMax] == NSOrderedDescending) {
mostCommon = word;
curMax = words[word];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
This should be significantly faster than my answer pre-edit, though I don't know how it compares to using the NSCountedSet answer.
Try using NSPredicate.
NSUInteger count=0;
NSString *mostCommonStr;
for(NSString *strValue in stringArray) {
NSUInteger countStr=[[stringArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self MATCHES[CD] %#, strValue]]count];
if(countStr > count) {
count=countStr;
mostCommonStr=strValue;
}
}
NSLog(#"The most commonstr is %#",mostCommonStr);

NSMutableString to NSMutableArray

I have a NSMutableString with a bunch of floating point numbers.
I need to convert them to NSMutableArray but in groups of 3:
0.015637 0.0205293 0.0270841 0.0157598 0.0202236 0.0272967 0.013217 0.0205293 0.0283439 0.0115028 0.0205293 0.0290539 0.0107803 0.0202236 0.0296187 -0.029892 0.0194995 0.0108798 -0.0299242 0.0191089 0.0108915 0.0243682 0.0194995 0.0204474 0.0307989 0.0205293 -0.00543068 0.0313996 0.0202236 0.00274711 -0.0157598 0.0202236 0.0272967 -0.0180789 0.0202236 0.0258193 -0.0182457 0.0194995 -0.0260576 -0.0182654 0.0191089 -0.0260857 -0.0134582 0.0191089
The numbers are all floating point values and there can be over 30 000 of them or more.
I have tried this but it's not working…
NSArray *contentarray = [content componentsSeparatedByString: #" "];
for(NSNumber * withfastenumeration in contentarray)
{
[XYZarray addObject: withfastenumeration];
}
Hi Guys,
I wanted to add a bit more information to this question since you guys have been so helpful. Okay so this is a COLLADA XML based project, and it involves vectors so hence the massive array of floating point numbers.
I got to the point where I am parsing with NSXMLParser and isolating the points. They are given however, in a blank format in the XML file as X, Y, Z in repetitive order, only separated by spaces.
I am writing a machining algorithm so I need these numbers in an array, but I need the array to look like:
X0.015637 Y0.0205293 Z0.0270841
X0.0157598 Y0.0202236 Z0.0272967
Once I have that, then I can manipulate all these numbers in a similar fashion.
Again, thanks for these solutions and Happy Holidays!
componentsSeparatedByString: returns an array of NSString, not an array of NSNumber. You need to write code to convert each component to a number. For example, you can send doubleValue to each component to get back a double, and then use #(...) to wrap the double in an NSNumber.
NSArray *words = [content componentsSeparatedByString:#" "];
NSMutableArray *triples = [NSMutableArray arrayWithCapacity:words.count / 3];
for (NSUInteger i = 0, count = words.count; i < count; i += 3) {
NSArray *triple = #[
#([words[i+0] doubleValue]),
#([words[i+1] doubleValue]),
#([words[i+2] doubleValue])
];
[triples addObject:triple];
}
Note that -[NSString doubleValue] doesn't do locale-aware parsing. If you need that, you'll need to use an NSScanner or NSNumberFormatter. Also, if you are having trouble with the memory used by the array of 30000+ substrings, use an NSScanner to process the substrings one at a time without creating the big array.
Here's how you do it with NSScanner:
NSScanner *scanner = [NSScanner scannerWithString:content];
// or use localizedScannerWithString: for locale-aware parsing
NSMutableArray *triples = [NSMutableArray arrayWithCapacity:words.count / 3];
double x, y, z;
while ([scanner scanDouble:&x] && [scanner scanDouble:&y] && [scanner scanDouble:&z]) {
NSArray *triple = #[ #(x), #(y), #(z) ];
[triples addObject:triple];
}
1st idea
what about this?
NSMutableString *_string = // your raw string as in your question...
NSLock *_lock = [[NSLock alloc] init];
NSMutableArray *_array = [NSMutableArray array];
[[_string componentsSeparatedByString:#" "] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([_lock tryLock]) [_array addObject:#([obj doubleValue])], [_lock unlock];
}];
2nd idea
that would be a better solution if you want to work with the numbers in group of 3 elegantly:
put these in the .h file:
typedef struct {
Float64 a;
Float64 b;
Float64 c;
} FloatNumbers;
static inline FloatNumbers FloatNumbersMake(Float64 a, Float64 b, Float64 c) {
FloatNumbers fn; fn.a = a; fn.b = b; fn.c = c; return fn;
}
static inline NSString * NSStringFromFloatNumbers(FloatNumbers fn) {
return [NSString stringWithFormat:#"{%f, %f, %f}", fn.a, fn.b, fn.c];
}
static inline FloatNumbers FloatNumbersFromString(NSString * string) {
#try {
NSArray *_array = [[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"{}"]] componentsSeparatedByString:#", "];
return FloatNumbersMake([[_array objectAtIndex:0] doubleValue], [[_array objectAtIndex:1] doubleValue], [[_array objectAtIndex:2] doubleValue]);
}
#catch (NSException *exception) {
return FloatNumbersMake(0.f, 0.f, 0.f);
}
}
and these into any methods of the .m file (where you'd like to parse the raw string):
NSMutableString *_string = // your raw string with the numbers...
NSError *_error;
NSLock *_lock = [[NSLock alloc] init];
NSMutableArray *_mutableArray = [NSMutableArray array];
NSRegularExpression *_regExp = [NSRegularExpression regularExpressionWithPattern:#"(([\\d\\.\\+-]+\\s?){0,3})" options:0 error:&_error];
if (!_error) {
[_regExp enumerateMatchesInString:_string options:NSMatchingReportProgress range:NSMakeRange(0, _string.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
FloatNumbers _floatNumbers = FloatNumbersMake(MAXFLOAT, MAXFLOAT, MAXFLOAT);
#try {
NSArray *_groupOfNumbers = [[_string substringWithRange:result.range] componentsSeparatedByString:#" "];
_floatNumbers.a = [[_groupOfNumbers objectAtIndex:0] doubleValue];
_floatNumbers.b = [[_groupOfNumbers objectAtIndex:1] doubleValue];
_floatNumbers.c = [[_groupOfNumbers objectAtIndex:2] doubleValue];
}
#catch (NSException *exception) {
}
#finally {
if ([_lock tryLock]) [_mutableArray addObject:NSStringFromFloatNumbers(_floatNumbers)], [_lock unlock];
*stop = (flags == NSMatchingHitEnd);
}
}];
} else {
NSLog(#"%#", _error);
}
the _mutableArray has the numbers, each object is a group of 3; here is an example of how you can read back the desired group of 3 numbers from the array and you are able to work with the values after.
// reading the numbers back
FloatNumbers _secondGroupOf3Numbers = FloatNumbersFromString([_mutableArray objectAtIndex:1]);
NSLog(#"a : %f, b : %f, c : %f", _secondGroupOf3Numbers.a, _secondGroupOf3Numbers.b, _secondGroupOf3Numbers.c);
therefore that will log you the second group of 3 numbers, like:
a : 0.015760, b : 0.020224, c : 0.027297
(bear in mind: those are rounded values for the debug-console only, the float numbers are the same as they were parsed.)
If I understand right you want to receive an array of strings and each string must contain 3 numbers?
Let's use regexp:
NSString *inputString = #"0.015637 0.0205293 0.0270841 0.0157598 0.0202236 0.0272967 0.013217 0.0205293 0.0283439 0.0115028 0.0205293 0.0290539 0.0107803 0.0202236 0.0296187 -0.029892 0.0194995 0.0108798 -0.0299242 0.0191089 0.0108915 0.0243682 0.0194995 0.0204474 0.0307989 0.0205293 -0.00543068 0.0313996 0.0202236 0.00274711 -0.0157598 0.0202236 0.0272967 -0.0180789 0.0202236 0.0258193 -0.0182457 0.0194995 -0.0260576 -0.0182654 0.0191089 -0.0260857 -0.0134582 0.0191089 0.0191089";
NSError *error;
// Creating regexp that will split input string to substring with 3 numbers
NSRegularExpression *regExp = [NSRegularExpression regularExpressionWithPattern:#"(([\\d\\.-]+\\s?){3})"
options:0
error:&error];
// TODO: Handle error if appear
//
NSArray *matches = [regExp matchesInString:inputString
options:0
range:NSMakeRange(0, [inputString length])];
NSMutableArray *result = [NSMutableArray new];
for (NSTextCheckingResult *match in matches) {
// Get the matching string
NSString *substring = [inputString substringWithRange:[match range]];
// Trim whitespace at the end
substring = [substring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[result addObject:substring];
}

Creating a NSArray from a C Array

There are many threads about going the opposite way, but I am interested in converting from a primitive C array to a NSArray. The reason for this is that I want to create a NSString from the array contents. To create the NSString I will use:
NSArray *array;
NSString *stringFromArray = [array componentsJoinedByString:#","];
I am joining the elements of the array by commas because I will later be saving the string as a .csv file. I don't think it matters, but the C array I am dealing with is of type double and size 43.
double c_array = new double [43];
Thanks!
NSString * stringFromArray = NULL;
NSMutableArray * array = [[NSMutableArray alloc] initWithCapacity: 43];
if(array)
{
NSInteger count = 0;
while( count++ < 43 )
{
[array addObject: [NSString stringWithFormat: #"%f", c_array[count]]];
}
stringFromArray = [array componentsJoinedByString:#","];
[array release];
}

Get matched string from two NSArrays

How can I save the string that match from one NSArray with one index difference in NSMutableArray?
For example, there are three "apple", four "pineapple", six "banana", two "cocoa" and the rest of words dont have duplicate(s) in the nsarray, i would like to know if the nsarray has at least two same words. If yes, I would like to save "apple", "pineapple, "banana" and "cocoa" once in nsmutablearray. If there are other alike words, I would like to add them to namutablearray too.
My code (which still doesn't work properly);
NSArray *noWords = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"words" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL]
componentsSeparatedByString:#"\n"]];
NSUInteger scount = [noWords count];
int ii = 0;
NSString *stringline;
for (ii; ii < scount; ii++)
{
stringline = [noWords objectAtIndex:ii];
NSLog(#"stringline : %# ", stringline);
}
int i = 1;
NSString *line;
for (i ; i < 10; i++)
{
line = [noWords objectAtIndex:i];
NSLog (#"line : %# ", line);
NSMutableArray *douwords = [NSMutableArray array];
if ([stringline isEqualToString:line])
{
NSString *newword;
for (newword in douwords)
{
[douwords addObject:newword];
NSLog (#"detected! %# ", douwords);
}
}
}
Here's a solution using two sets:
- (NSArray *)getDuplicates:(NSArray *)words
{
NSMutableSet *dups = [NSMutableSet set],
*seen = [NSMutableSet set];
for (NSString *word in words) {
if ([seen containsObject:word]) {
[dups addObject:word];
}
[seen addObject:word];
}
return [dups allObjects];
}
Assuming NSSet uses hash tables behind the scenes (which I'm betting it does), this is going to be faster than the previously suggested O(n^2) solution.
Here's something off the top of my head:
NSMutableSet* duplicates = [NSMutableSet set];
NSArray* words = [NSArray arrayWithObjects:#"Apple", #"Apple", #"Orange", #"Apple", #"Orange", #"Pear", nil];
[words enumerateObjectsUsingBlock:^(NSString* str, NSUInteger idx, BOOL *stop) {
for (int i = idx + 1; i < words.count; i++) {
if ([str isEqualToString:[words objectAtIndex:i]]) {
[duplicates addObject:str];
break;
}
}
}];
NSLog(#"Dups: %#", [duplicates allObjects]); // Prints "Apple" and "Orange"
The use of an NSSet, as opposed to an NSArray, ensures strings are not added more than once. Obviously, there are optimizations that could be done, but it should be a good starting point.
I assume that you want to count appearances of words in your array and output those with a count of more than one. A basic and verbose way to do that would be:
// Make an array of words - some duplicates
NSArray *wordList = [[NSArray alloc] initWithObjects:
#"Apple", #"Banana", #"Pencil",
#"Steve Jobs", #"Kandahar",
#"Apple", #"Banana", #"Apple",
#"Pear", #"Pear", nil];
// Make an mutable dictionary - the key will be a word from the list
// and the value will be a number representing the number of times the
// word appears in the original array. It starts off empty.
NSMutableDictionary *wordCount = [[NSMutableDictionary alloc] init];
// In turn, take each word in the word list...
for (NSString *s in wordList) {
int count = 1;
// If the word is already in the dictionary
if([wordCount objectForKey:s]) {
// Increse the count by one
count = [[wordCount objectForKey:s] intValue] + 1;
}
// Save the word count in the dictionary
[wordCount setObject:[NSNumber numberWithInt:count] forKey:s];
}
// For each word...
for (NSString *s in [wordCount keysOfEntriesPassingTest:
^(id key, id obj, BOOL *stop) {
if ([obj intValue] > 1) return YES; else return NO;
}]) {
// print the word and the final count
NSLog(#"%2d %#", [[wordCount objectForKey:s] intValue], s);
}
The output would be:
3 Apple
2 Pear
2 Banana