I'm building my own alternative to the Levenshtein distance to train myself in objective-c. This class will get a few sentences, only 2 during initial testing and return how many percent correct the sentence is. How many percent matches with the "correct sentence".
Basically what it does is it gets 2 sentences, (only 2 words during initial testing really) and calculates. But I get some strange NSLog's.
If I enter 1989 and 199 it'll return 75% correct - which is the correct answer. However, if I enter "orange" and "oange" - it returns 50% correct when correct is 83% (right??)
This is the code calling the method:
-(void)compare {
// Take each and every sentence from the users source and check it against the other sources. If it contains 2 or more words/numbers that are equal, i'll get "investigated" further
NSMutableArray *userSentences = [[MyManager sharedManager] contentSentencesList];
NSMutableArray *serverSentences = [NSMutableArray arrayWithArray:getSentencesFromText(serverText)];
// Sample sentences
[userSentences insertObject:#"oange" atIndex:userSentences.count];
[serverSentences insertObject:#"orange" atIndex:serverSentences.count];
// For-statement integers
int i = 0;
int b = 0;
for (i = 0; i < userSentences.count; i++) {
// Check first sentence
NSString *userSentence;
NSString *serverSentence;
// Check similarity of the two sentences, get percent and add to centerPercent
for (b = 0; b < serverSentences.count; b++) {
// Compare sentences
userSentence = userSentences[i];
serverSentence = serverSentences[b];
// Compare sentences with custom class
// Initialize Distance class
SourcerDistance *wordDistance = [[SourcerDistance alloc] init];
// Create resultPercent integer and calculate it
int resultPercent = [wordDistance distanceBetween:userSentence serverSentence:serverSentence];
NSLog(#"%#%d", #"FinishViewController result: ", resultPercent);
// Add resultPercent to averagePrecent and increase averagPercentToDivide by 1
centerPercent = centerPercent + resultPercent;
centerPercentToDivide++;
}
// Set and display resultoppositeSpelling
// averagePercent = centerPercent / centerPercentToDivide;
// Use an integer to remove decimals
[self presentResult];
}
}
and this is the other class:
#import "SourcerDistance.h"
#implementation SourcerDistance
-(int)distanceBetween:(NSString *)userSentence serverSentence:(NSString *)serverSentence {
// Declare the 2 arrays containing all the words from the user's source and the developer's source
NSArray *developerSourceSentence = [self getWords:serverSentence];
NSArray *userSourceSentence = [self getWords:userSentence];
// Declare variables that'll be use for for-statements
int developerWordsLoop = developerSourceSentence.count;
int userWordsLoop = userSourceSentence.count;
// Declare variables required for matching (average of all words)
float centerPercent = 0; // This is for ALL words in total
float centerPercentToDivide = 0; // This is for all words in total
// Single-word variables
float totalCharacters = 0;
float matchingCharacters = 0;
float percentMatchingSingleWord = 0;
NSLog(#"%#%d", #"userSourceSentenceCount: ", userSourceSentence.count);
NSLog(#"%#%d", #"developerSourceSentenceCount: ", developerSourceSentence.count);
// Loop through all of the user words
for (userWordsLoop = 0; userWordsLoop < userSourceSentence.count; userWordsLoop++) {
// Loop through all of the developer words
for (developerWordsLoop = 0; developerWordsLoop < developerSourceSentence.count; developerWordsLoop++) {
// Declare variables that contain all the characters of the user- and developer-words
NSMutableArray *userCharacters = [self getCharacters:userSourceSentence[userWordsLoop]];
NSMutableArray *developerCharacters = [self getCharacters:developerSourceSentence[developerWordsLoop]];
// Compare characters
matchingCharacters = [self compareCharacters:userCharacters developerCharacters:developerCharacters];
// Set the variables
totalCharacters = developerCharacters.count;
percentMatchingSingleWord = matchingCharacters / totalCharacters * 100;
NSLog(#"%#%f", #"totalCharacters", totalCharacters);
NSLog(#"%#%f", #"matchingCharacters", matchingCharacters);
NSLog(#"%#%f", #"iterate", percentMatchingSingleWord);
centerPercent = centerPercent + percentMatchingSingleWord;
centerPercentToDivide++;
}
}
// Declare variables used with final result
float finalPercentFloat = 0;
int finalPercent = 0;
NSLog(#"%#%f", #"centerPercent: ", centerPercent);
NSLog(#"%#%f", #"centerPercentToDivide: ", centerPercentToDivide);
finalPercentFloat = centerPercent/centerPercentToDivide;
NSLog(#"%#%f", #"finalPercent: ", finalPercentFloat);
finalPercent = (int)finalPercentFloat;
return finalPercent;
}
-(float)compareCharacters:(NSMutableArray *)userCharacters developerCharacters:(NSMutableArray *)developerCharacters {
// Declare result variables and other required variables
float matchingCharacters;
int userCharactersLoop = 0;
int developerCharactersLoop = 0;
// Loop through all of the userCharacters
for (userCharactersLoop = 0; userCharactersLoop < userCharacters.count; userCharactersLoop++) {
// Loop through all of the developerCharacters
for (developerCharactersLoop = 0; developerCharactersLoop < developerCharacters.count; developerCharactersLoop++) {
// Match every character here
if ([userCharacters[userCharactersLoop] isEqualToString:developerCharacters[developerCharactersLoop]]) {
// Increase matchingCharacters
matchingCharacters++;
} else {
// Do nothing
}
}
}
// Return result variable
return matchingCharacters;
}
-(NSArray *)getWords:(NSString *)sentence {
// Get words of sentence from developer-source
NSArray *sentenceWords;
NSString *serverSentenceToRead = sentence;
sentenceWords = [serverSentenceToRead componentsSeparatedByCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:#":;.!? "]
];
// Return developer words
return sentenceWords;
}
-(NSMutableArray *)getCharacters:(NSString *)word {
NSMutableArray *wordCharacters = [[NSMutableArray alloc] initWithCapacity:[word length]];
for (int i=0; i < [word length]; i++) {
NSString *ichar = [NSString stringWithFormat:#"%c", [word characterAtIndex:i]];
[wordCharacters addObject:ichar];
}
// Return the characters of the word
return wordCharacters;
}
#end
NSLog:
2014-09-03 20:22:32.015 Sourcer[27532:60b] userSourceSentenceCount: 1
2014-09-03 20:22:32.017 Sourcer[27532:60b] developerSourceSentenceCount: 1
2014-09-03 20:22:32.018 Sourcer[27532:60b] totalCharacters6.000000
2014-09-03 20:22:32.018 Sourcer[27532:60b] matchingCharacters3.001519
2014-09-03 20:22:32.019 Sourcer[27532:60b] iterate50.025322
2014-09-03 20:22:32.020 Sourcer[27532:60b] centerPercent: 50.025322
2014-09-03 20:22:32.021 Sourcer[27532:60b] centerPercentToDivide: 1.000000
2014-09-03 20:22:32.021 Sourcer[27532:60b] finalPercent: 50.025322
2014-09-03 20:22:32.022 Sourcer[27532:60b] FinishViewController result: 50
2014-09-03 20:22:32.022 Sourcer[27532:60b] averagePercent (float): 50.000000
What am I doing wrong here? Is it possible for anyone to understand the code and help me find out what's wrong? There's something odd about this algorithm
Thanks a lot!
(I know I'm kinda reinventing the wheel, but I want to try :))
Sincerely,
Erik
Related
I am making a game that requires me to use very large numbers. I believe I am able to store very large numbers with NSDecimal. However, when displaying the numbers to users I would like to be able to convert the large number to a succinct string that uses characters to signify the value eg. 100,000 -> 100k 1,000,000 -> 1.00M 4,200,000,000 -> 4.20B and so forth going up to extremely large numbers. Is there any built in method for doing so or would I have to use a bunch of
NSDecimalCompare statements to determine the size of the number and convert?
I am hoping to use objective c for the application.
I know that I can use NSString *string = NSDecimalString(&NSDecimal, _usLocale); to convert to a string could I then do some type of comparison on this string to get the result I'm looking for?
Use this method to convert your number into a smaller format just as you need:
-(NSString*) suffixNumber:(NSNumber*)number
{
if (!number)
return #"";
long long num = [number longLongValue];
int s = ( (num < 0) ? -1 : (num > 0) ? 1 : 0 );
NSString* sign = (s == -1 ? #"-" : #"" );
num = llabs(num);
if (num < 1000)
return [NSString stringWithFormat:#"%#%lld",sign,num];
int exp = (int) (log(num) / 3.f); //log(1000));
NSArray* units = #[#"K",#"M",#"G",#"T",#"P",#"E"];
return [NSString stringWithFormat:#"%#%.1f%#",sign, (num / pow(1000, exp)), [units objectAtIndex:(exp-1)]];
}
Some sample examples:
NSLog(#"%#",[self suffixNumber:#99999]); // 100.0K
NSLog(#"%#",[self suffixNumber:#5109999]); // 5.1M
Source
Solved my issue: Can only be used if you know that your NSDecimal that you are trying to format will only be a whole number without decimals so make sure you round when doing any math on the NSDecimals.
-(NSString *)returnFormattedString:(NSDecimal)nsDecimalToFormat{
NSMutableArray *formatArray = [NSMutableArray arrayWithObjects:#"%.2f",#"%.1f",#"%.0f",nil];
NSMutableArray *suffixes = [NSMutableArray arrayWithObjects:#"k",#"M",#"B",#"T",#"Qa",#"Qi",#"Sx",#"Sp",#"Oc",#"No",#"De",#"Ud",#"Dud",#"Tde",#"Qde",#"Qid",#"Sxd",#"Spd",#"Ocd",#"Nvd",#"Vi",#"Uvi",#"Dvi",#"Tvi", nil];
int dick = [suffixes count];
NSLog(#"count %i",dick);
NSString *string = NSDecimalString(&nsDecimalToFormat, _usLocale);
NSString *formatedString;
NSUInteger characterCount = [string length];
if (characterCount > 3) {
NSString *trimmedString=[string substringToIndex:3];
float a;
a = 100.00/(pow(10, (characterCount - 4)%3));
int remainder = (characterCount-4)%3;
int suffixIndex = (characterCount + 3 - 1)/3 - 2;
NSLog(#"%i",suffixIndex);
if(suffixIndex < [suffixes count]){
NSString *formatSpecifier = [formatArray[remainder] stringByAppendingString:suffixes[suffixIndex]];
formatedString= [NSString stringWithFormat:formatSpecifier, [trimmedString floatValue] / a];
}
else {
formatedString = #"too Big";
}
}
else{
formatedString = string;
}
return formatedString;
}
data = (
{
date = "2016-01-20";
"end_time" = "11:10:00";
"function_code" = RCV;
"operator_id" = JOHN;
"start_time" = "11:00:00";
"total_time" = 10;
"total_units" = 19;
},
{
date = "2016-01-20";
"end_time" = "12:25:00";
"function_code" = PIK;
"operator_id" = JOHN;
"start_time" = "12:15:00";
"total_time" = 10;
"total_units" = 26;
}
)
this array containing 2 dictionary ,i have to get the endtime from the first dictionary and starttime from the second dictionary and i want to calculate the break time from this value.i know how to get the value dictionary . data[0][#"end_time"] and data[1][#"start_time"] this is sufficient if array contains two elements .but if array has more than 5 means,i want to iterate the array .i will update my code what i did ...
my array name is arrData
for (int i 0; i<arrData.count; i++) {
dictData =arrData[i];
NSString *startTime =[dictData objectForKey:#"start_time"];
NSString *totalTime = [dictData objectForKey:#"total_time"];
NSNumber *numTotalUnits =[dictData objectForKey:#"total_units"];
NSString *functionCode = [dictData objectForKey:#"function_code"];
NSString *endTime = [dictData objectForKey:#"end_time"];
NSLog(#"%#",endTime);
[array1 addObject:endTime];
if (arrData[i+1] > arrData.count) {
dictData = arrData[i+1];
NSLog(#"%#",dictData);
NSString *strStartTime = [dictData objectForKey:#"start_time"];
NSLog(#"%#",strStartTime);
[array1 addObject:strStartTime];
NSLog(#"%#",array1);
}
}
i tried this coding but i got error like index 2 beyond bounds[0..1]
Try for (int i = 0; i < arrData.count - 1; i++) {.
In your current code, you let i become equal to arrData.count - 1 in the loop, which means that when you check i + 1 you're looking past the final array element.
I am using SceneKit, and I have an issue:
How can I extract the data from a SCNGeometryElement object ?
I use this method :
- (void)geometryElements:(SCNNode *)node {
for (int indexElement = 0; indexElement < node.geometry.geometryElementCount; indexElement++) {
SCNGeometryElement *currentElement = [node.geometry geometryElementAtIndex:indexElement];
NSLog(#"\n");
NSLog(#"bytes per index : %d", currentElement.bytesPerIndex);
NSLog(#"number element : %d", currentElement.primitiveCount);
NSLog(#"data lenght : %d", currentElement.data.length);
for (int indexPrimitive = 0; indexPrimitive < currentElement.primitiveCount; indexPrimitive++) {
int array[3];
memset(array, 0, 3);
[currentElement.data getBytes:&array range:NSMakeRange(indexPrimitive * 3, (currentElement.bytesPerIndex * 3))];
NSLog(#"currentelement : %d %d %d", array[0], array[1], array[3]);
}
}
The result is not good :
2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14539995 -1068223968 -379286778
2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14737374 -1068223968 -379286778
2015-04-10 15:10:25.183 IKTest[1234:244778] currentelement : 14934753 -1068223968 -379286778
Thanks in advance.
a few notes:
you should rely on geometryElement.primitiveType instead of hard coding 3 (unless you are sure that you're always dealing with SCNGeometryPrimitiveTypeTriangles)
it seems that the range's location does not take geometryElement.bytesPerIndex into account
your buffer is of size 3 * sizeof(int) but should be of size numberOfIndicesPerPrimitive * geometryElement.bytesPerIndex
As mnuages said, you should confirm primtive type and data type of index first.
Your code only work if index type is int.
Here is some code work for me. I only deals that geometry consisted of triangles.
void extractInfoFromGeoElement(NSString* scenePath){
NSURL *url = [NSURL fileURLWithPath:scenePath];
SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:nil];
SCNGeometry *geo = scene.rootNode.childNodes.firstObject.geometry;
SCNGeometryElement *elem = geo.geometryElements.firstObject;
NSInteger componentOfPrimitive = (elem.primitiveType == SCNGeometryPrimitiveTypeTriangles) ? 3 : 0;
if (!componentOfPrimitive) {//TODO: Code deals with triangle primitive only
return;
}
for (int i=0; i<elem.primitiveCount; i++) {
void *idxsPtr = NULL;
int stride = 3*i;
if (elem.bytesPerIndex == 2) {
short *idxsShort = malloc(sizeof(short)*3);
idxsPtr = idxsShort;
}else if (elem.bytesPerIndex == 4){
int *idxsInt = malloc(sizeof(int)*3);
idxsPtr = idxsInt;
}else{
NSLog(#"unknow index type");
return;
}
[elem.data getBytes:idxsPtr range:NSMakeRange(stride*elem.bytesPerIndex, elem.bytesPerIndex*3)];
if (elem.bytesPerIndex == 2) {
NSLog(#"triangle %d : %d, %d, %d\n",i,*(short*)idxsPtr,*((short*)idxsPtr+1),*((short*)idxsPtr+2));
}else{
NSLog(#"triangle %d : %d, %d, %d\n",i,*(int*)idxsPtr,*((int*)idxsPtr+1),*((int*)idxsPtr+2));
}
//Free
free(idxsPtr);
}
}
As the original question has a Swift tag, I am posting my solution in Swift 5.
extension SCNGeometryElement {
/// Gets the `Element` vertices
func getVertices() -> [SCNVector3] {
func vectorFromData<UInt: BinaryInteger>(_ float: UInt.Type, index: Int) -> SCNVector3 {
assert(bytesPerIndex == MemoryLayout<UInt>.size)
let vectorData = UnsafeMutablePointer<UInt>.allocate(capacity: bytesPerIndex)
let buffer = UnsafeMutableBufferPointer(start: vectorData, count: primitiveCount)
let stride = 3 * index
self.data.copyBytes(to: buffer, from: stride * bytesPerIndex..<(stride * bytesPerIndex) + 3)
return SCNVector3(
CGFloat.NativeType(vectorData[0]),
CGFloat.NativeType(vectorData[1]),
CGFloat.NativeType(vectorData[2])
)
}
let vectors = [SCNVector3](repeating: SCNVector3Zero, count: self.primitiveCount)
return vectors.indices.map { index -> SCNVector3 in
switch bytesPerIndex {
case 2:
return vectorFromData(Int16.self, index: index)
case 4:
return vectorFromData(Int.self, index: index)
case 8:
return SCNVector3Zero
default:
return SCNVector3Zero
}
}
}
}
There might be a bit of an indentation problem here, but yes, it is a function inside another one. The objective is the same as the other answers. Create a buffer, define what types of numbers the buffer is going to handle, build the SCNVector3 and return the array.
Through the code below, I can get an output such as :
0
1
1
What I want is to output the sum of these booleans values, in my case the result will be : 2 because we have 0+1+1
The code [Update] :
-(void)markers{
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
BOOL test3 = [test containsCoordinate:tg];
{
if (test3 == 1)
{
marker.position = CLLocationCoordinate2DMake(location.latitude, location.longitude);
}else if (test3 == 0)
{
marker.position = CLLocationCoordinate2DMake(0, 0);
}
}
}
}
Rather than sum BOOLs, which is counterintuitive, loop over whatever you are using to get the BOOL values, and if you get YES, increment a counter. This will be the number of YESs that you have.
If you have an array of BOOLs, you could just filter the array with a predicate to get the YES values and the length of the resulting array is the number of YESs that you have.
Edited to add code samples following OP comments
Incrementing a counter
NSUInteger numberOfBools = 0;
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
if ([test containsCoordinate:tg1]) { ++numberOfBools; }
if ([test containsCoordinate:tg2]) { ++numberOfBools: }
... // other tests here;
// numberOfBools now contains the number of passing tests.
Edited Again, after the full code was added
// snipped code above here
// This is where you add the counter and initialise it to 0
NSUInteger numberOfBools = 0;
for (NSDictionary *dictionary in array)
{
// Snip more code to this point
BOOL test3 = [test containsCoordinate:tg];
{
if (test3)
{
// This is where you increment the counter
++numberOfBools;
// Snip more code to the end of the for block
}
// Now numberOfBools shows how many things passed test3
int sum = (test3 ? 1 : 0) + (testX ? 1 : 0) + (testY ? 1 : 0);
And not so weird variant:
#define BOOL_TO_INT(val) ((val) ? 1 : 0)
int sum = BOOL_TO_INT(test3) + BOOL_TO_INT(testX) + BOOL_TO_INT(testY);
You can just add BOOLs since bools are just integers. e.g. :
int sum = 0;
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
BOOL test3 = [test containsCoordinate:tg];
//Obtain bolean values :
BOOL testX = /* other test */;
BOOL testY = /* other test */;
sum = test3 + testX + testY
This is a bad idea however, as BOOLs aren't necessarily 1 or 0. They are 0 and not 0
BOOL is just a typedef-ed char: typedef signed char BOOL; YES and NO are 1 and 0, but BOOL variable = 2 is perfectly valid
For example:
- (int) testX
{
if(inState1) return 1;
if(inState2) return 2;
else return 0;
}
BOOL textXResult = [self testX]; //Might return 2, this is still equivalent to YES.
The best solution is to iterate your BOOLs and instead count the number of YESes.
Another way to do this is to assume that if any one value is false, then the entire array is false, so, loop over the array until a false value is found, then break:
BOOL retval = true; //return holder variable
/*'boolsNumArray' is an NSArray of NSNumber instances, converted from BOOLs:
//BOOL-to-NSNumber conversion (makes 'boolsNumArray' NSArray, below)!
'[myNSArray addObject:[NSNumber numberWithBool:yourNextBOOL]]'
*/
for(NSNumber* nextNum in boolsNumArray) {
if([nextNum boolValue] == false) {
retval = false;
break;
}
}
return retval;
I'm trying to sort a JSON return into multiple sections according to each day. Here is the JSON return,
sales = (
{
"dis_dollars" = 0;
"dis_percent" = 0;
id = 111;
saleDay = 5;
saleMonth = 1;
saleTime = "20:02:39";
saleYear = 2014;
"total_price" = 25;
},
{
"dis_dollars" = 0;
"dis_percent" = 0;
id = 103;
saleDay = 2;
saleMonth = 1;
saleTime = "19:13:41";
saleYear = 2014;
"total_price" = 79;
},
{
"dis_dollars" = "0.55";
"dis_percent" = "0.10000000149012";
id = 101;
saleDay = 2;
saleMonth = 1;
saleTime = "10:41:11";
saleYear = 2014;
"total_price" = 22;
},
{
"dis_dollars" = 0;
"dis_percent" = 0;
id = 108;
saleDay = 1;
saleMonth = 1;
saleTime = "11:00:00";
saleYear = 2014;
"total_price" = 66;
}
);
}
I can get the number of sections if I do it this way
//...Connect to php script and get return json object
int saleDay = 0;
//Loop through json return
for (int i = 0;i < [sales count]; i++) {
NSDictionary* dict = [sales objectAtIndex:i];
Sale *eachSale =[[Sale alloc] initWithSaleId:[dict valueForKey:#"id"]
saleDay:[dict valueForKey:#"saleDay"]
saleMonth:[dict valueForKey:#"saleMonth"]
saleYear:[dict valueForKey:#"saleYear"]
saleTime:[dict valueForKey:#"saleTime"]
saleTotal:[dict valueForKey:#"total_price"]
saleDisDollars:[dict valueForKey:#"dis_dollars"]
saleDisPercent:[dict valueForKey:#"dis_percent"]];
NSLog(#"sale Day %#",eachSale.saleDay);
[_salesList addObject:eachSale];
int eachSaleDay = [eachSale.saleDay intValue];
// if the next day is different from the one before it, add a new section.
if (saleDay != eachSaleDay) {
_saleDaysCount += 1;
NSLog(#"eachSaleDay %i" ,eachSaleDay);
NSLog(#"salesDayCount %i" ,_saleDaysCount);
}
saleDay = eachSaleDay;
}
This will give me the correct number of sections, but how do I group the objects according to the day to find out the number of rows for each section, and what objects should go in each row?
I think I need to use NSSort Descriptors but I'm not sure where I would implement it and how to access the individual day arrays.
Any suggestions?
Thanks
I would simply add an additional field of type NSNumber with the complete date for each Sale while you are looping through your results.
eachSale.saleDateIdentifier = #(eachSale.saleYear.intValue * 10000 +
eachSale.saleMonth.intValue * 100 +
eachSale.saleDay.intValue);
Now it is easy to sort and count:
[_salesList sortUsingDescriptors:
#[[NSSortDescriptor sortDescriptorWithKey:#"saleDateIdentifier" ascending:NO]]];
NSArray *allDates = [_salesList valueForKeyPath:#"saleDateIdentifier"];
NSSet *uniqueDates = [NSSet setWithArray:allDates];
NSArray *sortedUniqueDates = [uniqueDates sortedArrayUsingDescriptors:
#[[NSSortDescriptor sortDescriptorWithKey:#"saleDateIdentifier" ascending:NO]]];
Your number of sections is sortedUniqueDates.count. For each section you could use the saleDateIdentifier to format the date string in titleForRowAtIndexPath.
BTW, maybe you do not want to use valueForKey in your init method for eachSale. Maybe you actually mean objectForKey. Better even, you should type the properties of the Sale class and use NSNumber and NSString as appropriate.