I have a list of Emoji entities and each of the has property codes and I want to check string ("]:-)") if it contains any of them and then replace smile with an image.
for (Emoji *emoji in self.emojis) {
for (NSString *code in emoji.codes) {
NSString *pattern = [NSRegularExpression escapedPatternForString:code];
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
NSArray *matches = [regex matchesInString:[sourceString string] options:0 range:NSMakeRange(0, [sourceString length])];
[matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult * _Nonnull aResult, NSUInteger idx, BOOL * _Nonnull stop) {
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
[attachment setImage:[UIImage imageNamed:emoji.image]];
NSAttributedString *replacement = [NSAttributedString attributedStringWithAttachment:attachment];
[sourceString replaceCharactersInRange:[aResult range] withAttributedString:replacement];
}];
}
}
The problem is that smile with code ]:-) contains :-) and my method replacing it with next: bracket ] + [image] for :-), it is because :-) goes first in list.
How can I check for exact string?
I've tried:
]:-/), \\b]:-/)\\b, /^]:-/)$/
Maybe there is better solution to make this working.
If I understood correctly your current structure:
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSArray *codes;
#end
A possible solution is instead to use a single couple of values: imageName/code
#interface Emoji : NSObject
#property (nonatomic, strong) NSString *image; //I'd expect a UIImage there and not an image name
#property (nonatomic, strong) NSString *code;
#end
self.emojis will have plenty of Emoji object that may have the same image name for different code, but an advantage of doing that is this little trick:
Sort self.emojis in a way that "smaller" emojis are at the end. So you'll replace first only the "lengthy" ones and the the smaller ones.
self.emojis = [arrayOfSingleEmojis sortedArrayUsingComparator:^NSComparisonResult(Emoji * _Nonnull emoji1, Emoji * _Nonnull emoji2) {
NSUInteger length1 = [[emoji1 code] length];
NSUInteger length2 = [[emoji2 code] length];
return [#(length2) compare:#(length1)]; //Or reverse length1 & length2, I never know, I always have to test, but I think it's the correct one
}];
So in your current case: ]:-) will be replace before :-) so you should have <imageFor:">:-)"] instead of ]<imageFor:":-)>
The following codes parsing JSON don't work any more for me after I update Mantle to 2.0. They can work fine on an older Mantle version( I don't remember the correct version number. What I know is I downloaded it in Nov of 2013.)
Here is the JSON content:
{
date = "2015-05-21";
error = 0;
results = (
{
currentCity = "beijing";
index = (
{
des = "desc1";
tipt = "tipt1";
title = "title1";
zs = "zs1";
},
{
des = "desc2";
tipt = "tipt2";
title = "title2";
zs = "zs2";
},
{
des = "desc3";
tipt = "tipt3";
title = "title3";
zs = "zs3";
}
);
}
);
status = success;
}
The Model I defined:
// .h
#import "MTLModel.h"
#import "Mantle.h"
#interface BaiduWeatherResults : MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSNumber *error;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *date;
#property (nonatomic, strong) NSString *currentCity;
#end
// .m
#import "BaiduWeatherResults.h"
#implementation BaiduWeatherResults
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"error" : #"error",
#"status" : #"status",
#"date" : #"date",
#"currentCity" : #"results.currentCity",
};
}
+ (NSValueTransformer *) currentCityJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSArray *values) {
return [values firstObject];
} reverseBlock:^(NSString *str) {
return #[str];
}];
}
Parse JSON to Model
id results =[MTLJSONAdapter modelOfClass:[BaiduWeatherResults class]
fromJSONDictionary:responseObject
error:nil];
NSLog(#"results:%#", results);
My Question:
The codes can work on an older Mantle. On the Mantle 2.0, the parse failed once I added #"currentCity" : #"results.currentCity" into the dictionary returned by JSONKeyPathsByPropertyKey . Anyone know what I missed for the parsing?
BTW, the currentCityJSONTransformer did call when the parse began. But the transformer is never used, because the line "return [values firstObject];" is never executed.
Thanks in advance.
Try this -
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"error" : #"error",
#"status" : #"status",
#"date" : #"date",
#"currentCity" : #"results",
};
}
+ (NSValueTransformer *) currentCityJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSArray *values) {
NSDictionary *cityInfo = [values firstObject];
return cityInfo[#"currentCity"];
} reverseBlock:^(NSString *str) {
return #[#{#"currentCity" : str}];
}];
}
Since results is an array of dictionaries, you can't access currentCity via dot syntax in JSONKeyPathsByPropertyKey. Instead the currentCityJSONTransformer finds the first dictionary in the results array and returns its value for currentCity. You might want to add type-checking and define the #"currentCity" key in a single place.
I mainly program in Java and can't understand why this isn't working. I'm trying to create a temporary object "Judge" in my for loop. I then want to add that object to an NSMutableArray so in the end I have an array filled with different Judge objects. After the for loop I run through all the objects in the Array and they're all the last Judge Object.
The NSLog shows that "JudgeTemp" object is being assigned the right values while in the for loop. My guess is that it's not creating a new object called JudgeTemp every time but referencing the old already created JudgeTemp.
NSMutableArray *Judges = [NSMutableArray arrayWithCapacity:30];
for (int i=0; i<[courtinfoarray count]; i++) {
Judge1= [[courtinfoarray objectAtIndex:i] componentsSeparatedByString:#"|"];
Judge *JudgeTemp=[[Judge alloc]init];
[JudgeTemp setName:[Judge1 objectAtIndex:0] picture:[Judge1 objectAtIndex:1] courtroom:[Judge1 objectAtIndex:2] phone:[Judge1 objectAtIndex:3] undergrad:[Judge1 objectAtIndex:4] lawschool:[Judge1 objectAtIndex:5] opdasa:[Judge1 objectAtIndex:6] career:[Judge1 objectAtIndex:7] judgecode:[Judge1 objectAtIndex:8]];
NSLog(#"%#",[JudgeTemp getName]);
[Judges addObject:JudgeTemp];
NSLog(#"%#",[[Judges objectAtIndex:i]getName]);
}
Judges Class
#implementation Judge
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
-(void) setName:(NSString *)n picture:(NSString *) p courtroom:(NSString *)c phone:(NSString *)ph undergrad: (NSString *) u lawschool: (NSString *)l opdasa: (NSString *) o career: (NSString *)ca judgecode: (NSString *)jcode{
name = n;
picture = p;
courtroom = c;
phone = ph;
undergrad = u;
lawschool = l;
opdasa = o;
career = ca;
judgecommentcode = jcode;
}
-(NSString*) getName{
return name;
}
The problem is with your Judge class. When you define variables directly in your #implementation they have global scope and are not instance variables. What you need to do is put those variable declarations in your #interface instead:
#interface Judge : NSObject {
NSString *name;
NSString *picture;
NSString *courtroom;
NSString *phone;
NSString *undergrad;
NSString *lawschool;
NSString *opdasa;
NSString *career;
NSString *judgecommentcode;
}
// ...
#end
Edit: Apparently you can declare them in your #implementation, you just have to wrap them in { }. See: Instance variables declared in ObjC implementation file
I have an iOS program that takes a string from the user, splits the string up by character, and then uses each character as a key to grab a seperate (morse code) character from a dictionary. I'm having issues regarding the char*s and strings, and I am receiving errors having to do with improper types and not finding the key in the array. Thoughts please? Thanks in advance.
Here is the code:
//
// ViewController.m
// MorseCodeTranslator
//
// Created by Mitch on 4/30/14.
// Copyright (c) 2014 Mitch . All rights reserved.
//
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *userInput;
#property (weak, nonatomic) IBOutlet UILabel *outputField;
- (IBAction)translateUserString:(UIButton *)sender;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)translateUserString:(UIButton *)sender {
BOOL stringPresent = (self.userInput.text.length > 0);
if (stringPresent) {
NSDictionary *morseCharacterKey = #{
#"A" : #".-",
#"B" : #"-...",
#"C" : #"-.-.",
#"D" : #"-..",
#"E" : #".",
#"F" : #"..-.",
#"G" : #"--.",
#"H" : #"....",
#"I" : #"..",
#"J" : #".---",
#"K" : #"-.-",
#"L" : #".-..",
#"M" : #"--",
#"N" : #"-.",
#"O" : #"---",
#"P" : #".--.",
#"Q" : #"--.-",
#"R" : #".-.",
#"S" : #"...",
#"T" : #"-",
#"U" : #"..-",
#"V" : #"...-",
#"W" : #".--",
#"X" : #"-..-",
#"Y" : #"-.--",
#"Z" : #"--..",
#"1" : #".----",
#"2" : #"..---",
#"3" : #"...--",
#"4" : #"....-",
#"5" : #".....",
#"6" : #"-....",
#"7" : #"--...",
#"8" : #"---..",
#"9" : #"----.",
#"0" : #"-----",
#" " : #"/",
#"," : #"--..--",
#"." : #".-.-.-",
#"?" : #"..--..",
#"\'" : #".----.",
#"!" : #"-.-.--",
#"/" : #"-..-.",
#"(" : #"-.--.",
#")" : #"-.--.-",
#"&" : #".-...",
#":" : #"---...",
#";" : #"-.-.-.",
#"=" : #"-...-",
#"+" : #".-.-.",
#"-" : #"-....-",
#"_" : #"..--.-",
#"\"" : #".-..-.",
#"$" : #"...-..-",
#"#" : #".--.-."
};
NSString *userString = self.userInput.text;
NSString *outputString;
for (int i = 0; userString.length > i; i++){
char userCharacter = [userString characterAtIndex:i];
char morseCharacter = [morseCharacterKey objectForKey:morseCharacterKey[userCharacter]];
outputString stringByAppendingString:[morseCharacter];
self.outputField.text = outputString;
}
}
}
#end
Your code should use NSString:
for (int i = 0; userString.length > i; i++) {
NSString *userCharacter = [userString substringWithRange:NSMakeRange(i, 1)];
NSString *morseCharacter = morseCharacterKey[userCharacter];
outputString = [outputString stringByAppendingString:morseCharacter];
NSLog(outputString);
}
}
So:
#"A"
Is an NSString literal. So your dictionary keys:
NSDictionary *morseCharacterKey = #{
#"A" : #".-",
...
Are NSString*s. Which is fine, because keys are required to be objects.
You're getting the character as a char, which is not an object, and even if it was it isn't the same object as you used for your keys.
Instead what you want to do is something like this:
NSRange range;
range.location = i;
range.length = 1;
NSString* userCharacter = [userString substringWithRange:range];
And use userCharacter as your key. Note that your values are also NSString*s, so you should store the result of your dictionary lookup in an NSString as well.
You shouldn't be working with char. Try this:
NSString *userString = self.userInput.text;
NSMutableString *outputString = [NSMutableString string];
for (NSUInteger i = 0; i < userString.length; i++) {
NSString *letter = [userString substringWithRange:NSMakeRange(i, 1)];
NSString *morse = morseCharacterKey[letter];
[outputString appendingString:morse];
}
self.outputField.text = outputString;
Block based enumeration would clean this up a little bit. Also a NSMutableString would be better then using stringByAppendingString. Also since I use a mutableString we don't have to use the __block specify in order to append to it like we would if stringByAppending string was used.
NSString *string = #"...";
NSMutableString *output = [[NSMutableString alloc] init];
[string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
NSString *value = morseCharacterKey[substring];
[output appendString:value];
}];
I want to check if a string is in a const struct. The way I do it is like this:
In my MyClass.h:
extern const struct MyAttributes {
__unsafe_unretained NSString *attribute1;
__unsafe_unretained NSString *attribute2;
__unsafe_unretained NSString *attribute3;
__unsafe_unretained NSString *attribute4;
__unsafe_unretained NSString *attribute5;
} MyAttributes;
Then in my MyClass.m I have:
const struct MyAttributes MyAttributes = {
.attribute1 = #"attribute1",
.attribute2 = #"attribute2",
.attribute3 = #"attribute3",
.attribute4 = #"attribute4",
.attribute5 = #"attribute5"
};
...
- (void)someMethod
{
BOOL test1 = [self check:#"test"];
BOOL test2 = [self check:#"attribute1"];
}
- (BOOL)check:(NSString *)string
{
return [string isEqualToString:MyAttributes.attribute1] ||
[string isEqualToString:MyAttributes.attribute2] ||
[string isEqualToString:MyAttributes.attribute3] ||
[string isEqualToString:MyAttributes.attribute4] ||
[string isEqualToString:MyAttributes.attribute5];
}
Well, this works. But, is there a better way to implement - (void)check so that, if I update MyAttributes, I won't have to update - (void)check?
You can convert it to an Objective-C array and then see if it contains the string you're looking for:
- (BOOL) check: (NSString*) string {
// I renamed the variable MyAttributes to myAttributes, following naming conventions
__unsafe_unretained id* ptr;
struct MyAttributes* attrPtr= &myAttributes;
memcpy(&ptr, &attrPtr, sizeof(id*));
NSArray* array= [NSArray arrayWithObjects: ptr count: sizeof(MyAttributes)/sizeof(NSString*)];
return [array containsObject: string];
}
The C-style way is to treat the struct as a C-array:
- (BOOL)check:(NSString *)string {
BOOL result= NO;
// I renamed the variable MyAttributes to myAttributes, following naming conventions
NSString* __unsafe_unretained * strPtr;
struct MyAttributes* ptr= &myAttributes;
memcpy(&strPtr, &ptr, sizeof(NSString**));
for(size_t i=0; i<sizeof(MyAttributes)/sizeof(NSString*) && !result;i++) {
result= [string isEqualToString: strPtr[i] ];
}
return result;
}
PS: I used memcpy to avoid bridge-casting, since the strings are already retained in myAttributes.
you can dig in the direction of keeping struct items in NSArray
Possible answer:
Cocoa structs and NSMutableArray
then in - check method use containsObject: or array iteration to check if string is there.