user1644365
user1644365

Reputation: 59

How to replace multiple regular expression matches in NSString?

I would like to know how is it possible to replace specific characters of an NSString based on some regular expression ([A-G]#?). More analytically, I have a NSString full of chords and spaces
e.g. " C A A# B".

What I want is to change these chords based on a dictionary that I made:
@"A", @"A#",
@"A#", @"B",
@"B", @"C",
@"C", @"C#",
@"C#", @"D",
@"D", @"D#",
@"D#", @"E",
@"E", @"F",
@"F", @"F#",
@"F#", @"G",
@"G", @"G#",
@"G#", @"A",

meaning that
A -> A# and A#-> B.
I would also like to keep anything that exists after my basic chord, meaning:
Am -> A#m.

I have already successfully scanned the string and replaced the elements, with the ones I wanted, in a new string, however I can't figure out how can I do that while maintaining the spaces of my initial string.

So to sum things, basically I want this:
" C A A# B"
to become this:
" C# A# B C"

Thank you!

Upvotes: 0

Views: 1762

Answers (2)

Martin R
Martin R

Reputation: 539745

The following code replaces all chords from the dictionary while preserving everything else (so that "Am" is replaced by "A#m" as requested):

NSDictionary *transposeDict = @{
    @"A": @"A#", @"A#": @"B", @"B": @"C", @"C": @"C#", @"C#": @"D",
    @"D": @"D#", @"D#": @"E", @"E": @"F", @"F": @"F#", @"F#": @"G",
    @"G": @"G#", @"G#": @"A"
};

NSString *melody = @" C A A# B   Am";
NSMutableString *transposedMelody = [melody mutableCopy];

NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"([A-G]#?)"
                                                                       options:0
                                                                         error:NULL];

NSArray *matches = [regex matchesInString:melody options:0 range:NSMakeRange(0, [melody length])];

for (NSTextCheckingResult *match in [matches reverseObjectEnumerator]) {
    NSString *oldChord = [melody substringWithRange:match.range];
    NSString *newChord = transposeDict[oldChord];
    if (newChord != nil)
        [transposedMelody replaceCharactersInRange:match.range withString:newChord];
}


NSLog(@"old: %@", melody);
NSLog(@"new: %@", transposedMelody);

Output:

old:  C A A# B   Am
new:  C# A# B C   A#m

The array matches contains all ranges of matching substrings. These ranges are then processed in reverse order (from last match to first match), so that mutating the string (which might change the length) does not influence the locations of the remaining ranges.

Upvotes: 2

Mike Brant
Mike Brant

Reputation: 71384

I really don't see the benefit of regex here at all considering you are going to be replacing all items in the source string with varying values. Your best bet might be to simply parse the string into an array:

i.e.

NSArray *stringArray = [myString componentsSeparatedByString:@" "];

Then perform the mapping/replacement on each value in the array using whatever logic you chose (i.e. enumerator with hashtable).

And finally just join the elements back together:

NSString *joinedString = [mappedStringArray componentsJoinedByString:@" "];

Upvotes: 2

Related Questions