NSData compare to NSData in percents - iphone

I Have
NSData *object1 and another NSData *object2. How can I compare this objects by what percentage they are similar? For example: Object1 similar to Object2 in - 99%. Thanks.

Get the bytes in both cases and iterate through checking how many of them are equal.
uint8_t* bytes1 = (uint8_t*)[object1 bytes];
uint8_t* bytes2 = (uint8_t*)[object2 bytes];
NSUInteger sameCount = 0;
for (NSUInteger i = 0 ; i < MIN([object1 length], [object2 length]) ; ++i)
{
if (bytes1[i] == bytes2[i])
{
sameCount++;
}
}
double fractionSame = (double) sameCount / (double) MIN([object1 length], [object2 length]);
The above assumes if one data is longer than the other, you don't care about the excess.

It really depends on the logic. If you are, for example, trying to compare images (and their data is stored as NSData) then you need to write image comparison algorithms. If it is some other kind of data, then you need to define that semantics first. If all else fails I think #JeremyP answer should suffice.

There's no such thing for NSData. You'll need to write your own NSSortDescriptor thing, optimized for how you want to compare the contents of one NSData to another.
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSSortDescriptor_Class/Reference/Reference.html

Related

Convert a hash to NSString?

Using the Evernote API, I have an object which has an NSUInteger property called hash. For the specific object I'm looking at, this is equal to:
<f5b5444b 33e740b7 f9d49a3b ddb6a39c>
I want to convert this into an NSString. Doing this:
[NSString stringWithFormat:#"%d", noteResource.hash]
Gives me this:
530049088
How could I correctly convert the hash value to an NSString?
When you see something output as "<" 8 hex digits space .... ">", it's the result of logging a NSData object (NSLog(#"%#", myDataObject);). So I believe what you have is not an NSUInteger, but a NSData * object.
There is no built in method to convert between strings and data, you need to do it in code:
- (NSString *)dataToString:(NSData *)data
{
NSUInteger len = [data length];
NSMutableString *str = [NSMutableString stringWithCapacity:len*2];
const uint8_t *bptr = [data bytes];
while(len--) [str appendFormat:#"%02.2x", *bptr++];
return str;
}
If this works, you can write your own stringToData method reversing the above, if needed.

Encode/Decode struct object

I am studying this wavefront obj loader example: https://github.com/jlamarche/iOS-OpenGLES-Stuff/tree/master/Wavefront%20OBJ%20Loader
And in order to improve object loading speed - I decided to encode loaded objects as NSValue and then store in Core data. Later, when corresponding object needs to be shown again - I fetch from database necessary nsvalue and unarchieve it.
It's all working, but problem is - it's not as fast I hoped it would be.
One of the reasons - because, in that example are used struct objects.
I was not successful to encode/decode them, so I used NSMutableArray, in which I write all struct data, and later - I iterate through it to put back values to struct object.
For example, there is struct:
typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;
It is then defined:
typedef Vertex3D Vector3D;
But in OpenGLWaveFrontObject class it is used like this:
Vector3D *vertexNormals;
how can I encode/decode struct like this?
I've tried like this:
[coder encodeObject:[NSValue value:&vertexNormals withObjCType:#encode(Vector3D)] forKey:#"vertexNormals"];
or like this:
[coder encodeObject:[NSValue value:&vertexNormals withObjCType:#encode(Vector3D[30])] forKey:#"vertexNormals"];
but its not working - if successfully encoded, then when decoding - values are incorrect.
example, how I put back in array of struct necessary values:
vertices = malloc(sizeof(Vertex3D) * [verticesArray count]);
for(int i = 0; i < [verticesArray count]; i++)
{
vertices[i].x = [[[verticesArray objectAtIndex:i] objectAtIndex:0] floatValue];
vertices[i].y = [[[verticesArray objectAtIndex:i] objectAtIndex:1] floatValue];
vertices[i].z = [[[verticesArray objectAtIndex:i] objectAtIndex:2] floatValue];
}
this works, but I have few more struct arrays and in case arrays are big - this becomes a big issue.
EDIT:
Using Amin Negm-Awad provided answer:
If I simply change NSArchiver to NSKeyedArchiver - it throws error:
[NSKeyedArchiver encodeArrayOfObjCType:count:at:]: unsupported type "{?=fff}]" for array encoding'
When I try:
NSValue *encapsulated = [NSValue valueWithBytes:vertexNormals objCType:#encode(Vertex3D[3])];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encapsulated];
[coder encodeObject:data forKey:#"vertexNormals"];
And vertexNormals were created:
vertexNormals = calloc(3, sizeof(Vector3D));
and filled with similar data You provided.
EDIT 2:
Using updated answer which Amin Negm-Awad provided,
I was successfully able to store struct object as NSData and encode it, and after that - decode it. It works!
example, so that it might help some one else too:
//Encode
NSData *verticesData = [NSData dataWithBytes:vertices length:numberOfVertices * sizeof(Vector3D)];
[coder encodeObject:verticesData forKey:#"vertices"];
//Decode
vertices = malloc(sizeof(Vertex3D) * numberOfVertices);
NSData *verticesData = [decoder decodeObjectForKey:#"vertices"];
[verticesData getBytes:vertices length:sizeof(Vertex3D) * numberOfVertices];
where Vertex3D was a struct:
typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;
and used as a struct array (I don't know how it is really called):
Vertex3D *vertices;
Unfortunately I was not able to store texture data. I can encode and decode, but decoded data are always randomly bizarre.
I am declaring it in this way:
GLfloat *textureCoords;
And I Encoded/Decoded in this way:
//Encode
NSData *textureCoordsData = [NSData dataWithBytes:textureCoords length:valuesPerCoord * numberOfVertices];
[coder encodeObject:textureCoordsData forKey:#"textureCoords"];
//Decode
textureCoords = malloc(sizeof(GLfloat) * valuesPerCoord * numberOfVertices);
NSData *textureCoordsData = [decoder decodeObjectForKey:#"textureCoords"];
[textureCoordsData getBytes:textureCoords length:valuesPerCoord * numberOfVertices];
What could be the problem?
First of all: If the array has variable length, it is simply not possible to use NSValue. This is documented:
The type you specify must be of constant length. You cannot store C
strings, variable-length arrays and structures, and other data types
of indeterminate length in an NSValue—you should use NSString or
NSData objects for these types.
So you should really take into account to use NSData instead.
But, if you want to use NSValue (because you have a constant-length array):
Both of your approaches has an extra indirection. The first one has the wrong type, too. What should work:
// Create some data
Vertex3D *cArray = malloc(30 * sizeof(Vertex3D));
NSInteger i;
for (i=0; i<30; i++)
{
cArray[i].x = i;
cArray[i].y = 9811;
cArray[i].z = 29-i;
}
NSLog(#"%p", cArray);
// Encapsulate that in a value
// Have a look at the parameters
NSValue *encapsulated = [NSValue valueWithBytes:cArray objCType:#encode(Vertex3D[30])];
// Put it through a coder and store the coded data on disk
NSData *data = [NSArchiver archivedDataWithRootObject:encapsulated];
[data writeToFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here
// from disk
data = [NSData dataWithContentsOfFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath]];
encapsulated = [NSUnarchiver unarchiveObjectWithData:data];
Vertex3D *cArray2 = malloc(30 * sizeof(Vertex3D));
[encapsulated getValue:cArray2];
for (i=0; i<30; i++)
{
NSLog(#"%f %f %f", cArray[i].x, cArray[i].y, cArray[i].z);
}
NSLog(#"%p", cArray2);
This works in my test code
Update because of the problem with keyed achieving:
To store the c array in an instance of NSData instead of an instance of NSValue do this instead
After
NSLog(#"%p", cArray);
change the code to
// Convert to an NSData instance
NSData *data = [NSData dataWithBytes:cArray length:lengthInBytes];
[data writeToFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here
// from disk
data = [NSData dataWithContentsOfFile:[#"~/Desktop/valueTest" stringByExpandingTildeInPath]];
Vertex3D *cArray2 = malloc(lengthInBytes);
[data getBytes:cArray2 length:lengthInBytes];
until
for (i=0; i<30; i++)

Finding element in array

I have the follow code:
NSArray *myArray = [NSArray arrayWithObjects: #"e", #"è", #"é",#"i","ò",nil];
NSString *string = #"simpleè";
NSMutablestring *newString;
for(i=0>;i< [string length]; i++){
if([stringa characterAtIndex:i] is in Array){
[newString appendFormat:#"%c", [string characterAtIndex:i]];
}
}
How make finding if single char of string stay in the array?
Example of result:
newString= #"ieè";
I think you want to apply rangeOfCharacterFromSet:options:range: repeatedly. You'll have to create a NSCharacterSet from the characters in your array somehow.
Added
Though it probably would be just as simple to just loop through the string with characterAtIndex and compare each char (in an inner loop) to the chars in your array (which you could extract into a unichar array or put into a single NSString to make easier to access).
Umm... if you want to check what the values are you can use NSLog
NSLog"%f", myFloat;
So you can use this to check your array... Which is what I think you are asking for, but the grammar in your question isn't very good. Please provide more details and better grammar.
You should check the length of your string and then match your string characters with the array and if found append that character in a new string.
NSString *mstr = #"asdf";
NSString *b = [ mstr characterAtIndex:0];
Hope it helps.......
You'll want to create an NSCharacterSet with the characters in the string and then ask each string in the array for its rangeOfCharacterFromSet:. If you find one where a range was actually found, then a character from the string is in the array. If not, then none of them are.
This might seem a bit roundabout, but Unicode makes looking at strings as just a series of "chars" rather unreliable, and you'll want to let your libraries do as much of the heavy lifting for you as they can.
There are better ways to do this, but this is what I think you're trying to do:
NSMutableString* result= [NSMutableString stringWithString:#""];
for( int i= 0; i < [string length]; ++i ) {
NSString* c= [NSString stringWithFormat:#"%C", [string characterAtIndex:i]];
if( [myArray containsObject:c] )
[result appendString:c];
}

Error during insert binary data in Ultralite database iPhone

Hi I am writing a iphone application where I need to store binary data i.e.; image in the Ultralite database.
I am using following code for this purpose.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"file_name" ofType:#"png"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSUInteger len = [data length];
ul_binary *byteData = (ul_binary*)malloc(len);
memcpy(byteData, [data bytes], len);
ULTable *table = connection->OpenTable("NAMES");
if(table->InsertBegin()){
table->SetInt(1, (maxId+1));
table->SetString(2, [name UTF8String]);
table->SetBinary(3, byteData);
table->Insert();
table->Close();
connection->Commit();
}
This code is giving error 'EXC_BAD_ERROR' on line::
table->SetBinary(3, byteData);
This code works fine if i comment this line.
Any help would be appreciated!
Thanks
The definition of ul_binary is this:
typedef struct ul_binary {
/// The number of bytes in the value.
ul_length len;
/// The actual data to be set (for insert) or that was fetched (for select).
ul_byte data[ MAX_UL_BINARY ];
} ul_binary, * p_ul_binary;
So it's a struct. By simply doing the memcpy as you do, you also overwrite the len field and everythings messed up. So here's how you should do it (as far as I can see):
ul_binary *byteData = (ul_binary *)malloc(sizeof(ul_binary));
memcpy(&byteData->data, [data bytes], len);
byteData->len = len;
You also need to check that len <= MAX_UL_BINARY before you try to allocate the memory. And don't forget to free(byteData);.

Is this a valid way to build and send NSData through GameKit?

Its y first time trying to use NSData and Gamekit. So was wondering am i packing the data properly?
- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID
reliable:(BOOL)howtosend
{
// the packet we'll send is resued
static unsigned char networkPacket[kMaxTankPacketSize];
const unsigned int packetHeaderSize = 2 * sizeof(int); // we have two "ints" for our
header
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = gamePacketNumber++;
pIntData[1] = packetID;
int theLength = 2 * sizeof(int);
for (int i=0; i<([theHands.player1Hand count]); i++)
{
pIntData[2+i] = [[theHands.player1Hand objectAtIndex:i] intValue];
theLenght += sizeof(int);
}
NSData *packet = [NSData dataWithBytes: networkPacket length: theLength];
[session sendData:packet toPeers:[NSArray arrayWithObject:gamePeerId]
withDataMode:GKSendDataReliable error:nil];
}
Will the data I put into NSData *packet be valid?
Many Thanks,
-Code
You create the NSData correctly, and it will contain what you expect. But this is rather more complicated than necessary. The following will do too:
enum { kHeaderLength = 2 };
NSMutableData *packet = [NSMutableData dataWithLength: (kHeaderLength + [theHands.player1Hand count]) * sizeof( int )];
int *pIntData = (int *)[packet mutableBytes];
*pIntData++ = gamePacketNumber++;
*pIntData++ = packetID;
for (id thing in theHands.player1Hand) {
*pIntData++ = [thing intValue];
}
[session sendData: packet toPeers: [NSArray arrayWithObject: gamePeerId] withDataMode: GKSendDataReliable error: NULL];
This will have some advantages:
The packet data will not be copied to the NSData object, it will be created directly in there.
You don’t have to depend on a maximum packet size. You used a constant, which is good since you could change that in one place if bigger packets are required some time. But this is not really needed.
Your version is not thread safe since it depends on the static buffer. This might be fine, since not every method has to be thread-safe. But this is something one has to look out for.
Using fast enumeration also helps keeping the overhead down and is more readable.