I want to use -[NSArray componentsJoinedByString] with some condition for example : concatenate all elements of my array with ", " except the last one where I want an " and ".
It python it would be something like :
', '.join(array[:-1]) + ' and ' + array[-1]
Is there a way or a method that would do the trick in one line, avoiding all the if else stuff ?
You can use subarrayWithRange: and stringWithFormat: to do the same thing. You do need at least 1 if to check the count of items in the array and ensure that you don't have an index exception.
try like this,i dont know is this effiecient or not but check once,
NSArray *arr= [[NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSString *string = [arr componentsJoinedByString:#","];
NSString *str= [NSString stringWithFormat:#"%# and %#",[string substringToIndex:[string length]-[[arr lastObject] length]-1],[arr lastObject]];
NSLog(#"%#",str);
It might not be the most efficient, but it is a nice simple solution to take an array of objects that have string representations and turn them into a comma separated list such as 1, 2, 3, and 4.
#implementation NSArray (MyCollection)
- (NSString *)stringCommaAndSeparated
{
return [self stringCommaAndSeparatedUsingStringConverter:^NSString *(id object) {
assert( [object isKindOfClass:NSString.class] );
return (NSString *)object;
}];
}
- (NSString *)stringCommaAndSeparatedUsingStringConverter:(NSString *(^)(id object))stringConverter
{
assert( stringConverter );
if( self.count == 0 )
return #"";
if( self.count == 1 )
return stringConverter(self.firstObject);
if( self.count == 2 )
return [NSString stringWithFormat:NSLocalizedString(#"%# and %#", nil), stringConverter(self[0]), stringConverter(self[1]) ];
NSMutableString *string = [[NSMutableString alloc] init];
for( int index = 0; index < self.count-1; index += 1 )
[string appendFormat:NSLocalizedString(#"%#, ", nil), stringConverter(self[index])];
[string appendFormat:NSLocalizedString(#"and %#", nil), stringConverter(self.lastObject)];
return string; // This is returning a mutable string, but we could copy it to an immutable one!
}
#end
Here is an example of this in action:
// Results in #"1, 2, 3, and 4"
NSString *str = [#[#"1", #"2", #"3", #"4"] stringCommaAndSeparated];
// Results in #"1, 2, 3, and 4"
NSString *str2 = [#[#(1), #(2), #(3), #(4)] stringCommaAndSeparatedUsingStringConverter:^NSString *(id object) {
assert( [object isKindOfClass:NSNumber.class] );
NSNumber *number = object;
return number.stringValue;
}];
Here's my category solution, based on Wain's answer:
#implementation NSArray (NSString)
- (NSString *)listStringUsingLocale:(NSLocale *)locale
{
if(!self.count)
return nil;
if(!locale)
locale = [NSLocale currentLocale];
NSString *language = [locale objectForKey: NSLocaleLanguageCode];
if([language isEqualToString:#"en"])
{
if (self.count > 1)
{
NSArray *arr1 = [self subarrayWithRange:NSMakeRange(0, self.count - 1)];
return [NSString stringWithFormat:#"%# and %#", [arr1 componentsJoinedByString:#", "], self.lastObject];
}
else
{
return self[0];
}
}
return nil;
}
#end
I've added the option to add locale-based options.
Related
I'm trying to re-arrange words into alphabetical order. For example, tomato would become amoott, or stack would become ackst.
I've found some methods to do this in C with char arrays, but I'm having issues getting that to work within the confines of the NSString object.
Is there an easier way to do it within the NSString object itself?
You could store each of the string's characters into an NSArray of NSNumber objects and then sort that. Seems a bit expensive, so I would perhaps just use qsort() instead.
Here it's provided as an Objective-C category (untested):
NSString+SortExtension.h:
#import <Foundation/Foundation.h>
#interface NSString (SortExtension)
- (NSString *)sorted;
#end
NSString+SortExtension.m:
#import "NSString+SortExtension.h"
#implementation NSString (SortExtension)
- (NSString *)sorted
{
// init
NSUInteger length = [self length];
unichar *chars = (unichar *)malloc(sizeof(unichar) * length);
// extract
[self getCharacters:chars range:NSMakeRange(0, length)];
// sort (for western alphabets only)
qsort_b(chars, length, sizeof(unichar), ^(const void *l, const void *r) {
unichar left = *(unichar *)l;
unichar right = *(unichar *)r;
return (int)(left - right);
});
// recreate
NSString *sorted = [NSString stringWithCharacters:chars length:length];
// clean-up
free(chars);
return sorted;
}
#end
I think separate the string to an array of string(each string in the array contains only one char from the original string). Then sort the array will be OK. This is not efficient but is enough when the string is not very long. I've tested the code.
NSString *str = #"stack";
NSMutableArray *charArray = [NSMutableArray arrayWithCapacity:str.length];
for (int i=0; i<str.length; ++i) {
NSString *charStr = [str substringWithRange:NSMakeRange(i, 1)];
[charArray addObject:charStr];
}
NSString *sortedStr = [[charArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] componentsJoinedByString:#""];
// --------- Function To Make an Array from String
NSArray *makeArrayFromString(NSString *my_string) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < my_string.length; i ++) {
[array addObject:[NSString stringWithFormat:#"%c", [my_string characterAtIndex:i]]];
}
return array;
}
// --------- Function To Sort Array
NSArray *sortArrayAlphabetically(NSArray *my_array) {
my_array= [my_array sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
return my_array;
}
// --------- Function Combine Array To Single String
NSString *combineArrayIntoString(NSArray *my_array) {
NSString * combinedString = [[my_array valueForKey:#"description"] componentsJoinedByString:#""];
return combinedString;
}
// Now you can call the functions as in below where string_to_arrange is your string
NSArray *blowUpArray;
blowUpArray = makeArrayFromString(string_to_arrange);
blowUpArray = sortArrayAlphabetically(blowUpArray);
NSString *arrayToString= combineArrayIntoString(blowUpArray);
NSLog(#"arranged string = %#",arrayToString);
Just another example using NSMutableString and sortUsingComparator:
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:#"tomat"];
[mutableString appendString:#"o"];
NSLog(#"Orignal string: %#", mutableString);
NSMutableArray *charArray = [NSMutableArray array];
for (int i = 0; i < mutableString.length; ++i) {
[charArray addObject:[NSNumber numberWithChar:[mutableString characterAtIndex:i]]];
}
[charArray sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
if ([obj1 charValue] < [obj2 charValue]) return NSOrderedAscending;
return NSOrderedDescending;
}];
[mutableString setString:#""];
for (int i = 0; i < charArray.count; ++i) {
[mutableString appendFormat:#"%c", [charArray[i] charValue]];
}
NSLog(#"Sorted string: %#", mutableString);
Output:
Orignal string: tomato
Sorted string: amoott
I want to be able to combine each index so that I get #"Biology Teacher A BK 1", but so far I have been unsuccessful. This is what I have so far, but I do not know where to go from here.
#interface ListTableViewController () <UISearchDisplayDelegate>
#property (strong, nonatomic) NSArray *className;
#property (strong, nonatomic) NSArray *teacherName;
#property (strong, nonatomic) NSArray *blockNumber;
#end
#implementation ListTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.className = [NSArray arrayWithObjects:
#"Biology",
#"English III",
#"Chemistry",
#"Algebra II",
#"Morality", nil];
self.teacherName = [NSArray arrayWithObjects:
#"Teacher A",
#"Teacher B",
#"Teacher C",
#"Teacher D",
#"Teacher E", nil];
self.blockNumber = [NSArray arrayWithObjects:
#"BK 1",
#"BK 3",
#"BK 6",
#"BK 2",
#"BK 1", nil];
}
It Will work:
for (int i = 0 ; i< self.className.count; i++)
{
NSString *temStr = [NSString stringWithFormat:#"%# %# %#",[self.className objectAtIndex:i] ,[self.teacherName objectAtIndex:i],[self.blockNumber objectAtIndex:i] ];
NSLog("%#", tempStr);
}
Try this...
int total = self.className.count;
NSMutableArray *combinedName = [NSMutableArray array];
if (total == self.teacherName.count && total == self.blockNumber.count)
{
for(int i=0;i< total;i++)
{
NSString *str =[NSString stringWithFormat:#"%# %# %#", [self.className objectAtIndex:i],[self.teacherName objectAtIndex:i],[self. blockNumber objectAtIndex:i]];
[combinedName addObject:str];
}
}
else
NSLog(#"Cann't combine");
Try this
//Assuming three array are in same length
NSMutableArray *combineArray=[[NSMutableArray alloc] init];
for(int i=0; i<[[self className] count]; i++)
{
[combineArray addObject:[NSString stringWithFormat:#"%# %# %#", [[self className] objectAtIndex:i],[[self teacherName] objectAtIndex:i], [[self blockNumber] objectAtIndex:i]];
}
NSLog(#"%#", combineArray); //here is your output.
You can try this:
NSMutableArray *combinedArray = [[NSMutableArray alloc]init];
for (int i = 0; i < [self.className count]; i++)
{
NSString *combinedString = [NSString stringWithFormat:#"%# %# %#",[self.className objectAtIndex:i],[self.teacherName objectAtIndex:i],[self. blockNumber objectAtIndex:i]];
[combinedArray addObject:combinedString];
}
NSLog(#"Combined array is :\n %#",combinedArray);
Try with following code:
for (int i = 0 ; i< self.className.count; i++)
{
NSString *temStr = [[NSString stringWithFormat:#"%# ", [self.className objectAtIndex:i]] stringByAppendingString:[NSString stringWithFormat:#"%# ", [self.className objectAtIndex:i]]]
NSLog("%#", [temStr stringByAppendingString:[self.blockNumber objectAtIndex:i]])
}
Something like this would work, fairly ugly though.
self.className = [NSArray arrayWithObjects:#"Biology",#"English III",#"Chemistry",#"Algebra II",#"Morality", nil];
self.teacherName = [NSArray arrayWithObjects:#"Teacher A",#"Teacher B",#"Teacher C",#"Teacher D",#"Teacher E", nil];
self.blockNumber = [NSArray arrayWithObjects:#"BK 1",#"BK 3",#"BK 6",#"BK 2",#"BK 1", nil];
NSMutableArray *combinedNames = [[NSMutableArray alloc] init];
if (([self.className count] == [self.teacherName count]) && [self.className count] == [self.blockNumber count]) {
for (int index = 0; index < [self.className count]; index++) {
[combinedNames addObject:[NSString stringWithFormat:#"%# %# %#", [self.className objectAtIndex:index], [self.teacherName objectAtIndex:index], [self.blockNumber objectAtIndex:index]]];
}
}
for (NSString *string in combinedNames) {
NSLog(#"%#", string);
}
and that outputs:
Biology Teacher A BK 1
English III Teacher B BK 3
Chemistry Teacher C BK 6
Algebra II Teacher D BK 2
Morality Teacher E BK 1
Update
Looks like this was posted already by others before I could finish getting it put together. I don't see that they verify that the array's are all of the same length though. You can use anyones answer; it might be wise to verify that all of the array's contain the same number of objects prior to trying to iterate through them.
I have an NSString for example "This is my question".I want to find all the indices of the character/substring "i" ie In this case If index starts from 0,then I want 2,5,16 as my answer.
The other answer is a bit of an overkill. Why don't you simply iterate over the characters like this:
NSString *x = #"This is my question";
for (NSUInteger i=0;i<[x length];i++)
{
if ([x characterAtIndex:i]=='i')
{
NSLog(#"found: %d", i);
}
}
It outputs exactly your positions:
found: 2
found: 5
found: 16
I'd like suggest my solution. It is like this:
NSString* str = #"This is my question";
NSArray* arr = [str componentsSeparatedByString: #"i"];
NSMutableArray* marr = [NSMutableArray arr];
NSInteger cnt = 0;
for (NSInteger i = 0; i < ([arr count]); i++)
{
NSString* s = [arr objectAtIndex: i];
cnt += [s length];
[marr addObject: [NSNumber numberWithInt: cnt]];
cnt += [#"i" length];
}
NSLog(#"%#", [marr description]);
On console:
2
5
16
I don't know is there any built-in functions available for doing this. You can use this method:
- (NSMutableArray *)indexOfCharacter:(char)c inString:(NSString*)string
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for(int i=0;i<string.length;i++)
{
if(c == [string characterAtIndex:i])
{
[returnArray addObject:[NSNumber numberWithInt:i]];
}
}
return returnArray;
}
Using NSRange and loop and with some string manipulation you can easily do it.
NSString *string = #"This is my question";
NSString *substring = #"i";
NSRange searchRange = NSMakeRange(0,string.length);
NSRange foundRange;
while (searchRange.location < string.length)
{
searchRange.length = string.length-searchRange.location;
foundRange = [string rangeOfString:substring options:nil range:searchRange];
if (foundRange.location != NSNotFound)
{
// found an occurrence of the char
searchRange.location = foundRange.location+foundRange.length;
NSLog(#"Location of '%#' is %d",substring,searchRange.location-1);
}
}
EDIT
Using NSRegularExpression and NSRange you can do like this.
NSString *string = #"This is my question";
NSString *substring = #"i";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:substring
options:0
error:NULL];
[regex enumerateMatchesInString:string options:0 range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange range = [result range];
NSLog(#"Location of '%#' is %d",substring, range.location);
}];
output is
Location of 'i' is 2
Location of 'i' is 5
Location of 'i' is 16
This is my attempt at a no loop code of getting what you want. I coded this blind, meaning not-tested etc. Its basically a recursive function, but I think it gets you the general idea.
- (NSArray *)getAllEyes:(NSString *)s index:(int)index) {
if (!s || s.length <= 0 || index >= s.length) return [NSArray new];
NSRange *r = [s rangeOfString(#"i") options:NSLiteralSearch range:NSMakeRange(index, s.length - index)];
if (r.location == NSNotFound) {
return [NSArray new];
} else {
NSMutableArray *array = [NSMutableArray new];
[array addObject:#(r.location)];
[array addObjectsFromArray:[self getAllEyes:s index:r.location + 1]];
return array;
}
}
// usage:
NSArray *allEyes = [self getAllEyes:#""];
for (NSNumber *n in allEyes) {
NSLog(#"i = %#", n);
}
I have data exported to excel it works fine.
But I have a little question
My output is exported like this:
What i would like to happen is this:
and this is my code to export:
-(void)exportCSV {
NSArray * data = [NSArray arrayWithObjects:entries,keys, nil];
NSLog(#"%#",data);
csv =[NSMutableString string];
for (NSArray * line in data) {
NSMutableArray * formattedLine = [NSMutableArray array];
for ( field in line) {
BOOL shouldQuote = NO;
NSRange r = [field rangeOfString:#","];
//fields that contain a , must be quoted
if (r.location != NSNotFound) {
shouldQuote = YES;
}
r = [field rangeOfString:#"\""];
//fields that contain a " must have them escaped to "" and be quoted
if (r.location != NSNotFound) {
field = [field stringByReplacingOccurrencesOfString:#"\"" withString:#"\"\""];
shouldQuote = YES;
}
if (shouldQuote == YES) {
[formattedLine addObject:[NSString stringWithFormat:#"\"%#\"\"%#\"", entries,keys]];
} else {
[formattedLine addObject:field];
}
}
NSString * combinedLine = [formattedLine componentsJoinedByString:#";"];
[csv appendFormat:#"%#\n", combinedLine];
NSLog(#"%#",csv);
}
}
Does the following do what you want?
Note that I have not considered quotation, I leave that up to you ;)
Also note that I assume that entries.count == keys.count
- (void)exportCSV {
NSArray *keys = #[#"T", #"R", #"RTT"];
NSArray *entries = #[#"-329180696", #"1243918297", #"-998693494"];
NSMutableString *csv = [[NSMutableString alloc] initWithCapacity:0];
for (int i = 0; i < entries.count; i++) {
[csv appendFormat:#"%#;%#\n", keys[i], entries[i]];
}
}
Output:
T;-329180696
R;1243918297
RTT;-998693494
I have a sorted array of NSString values, I would like to know how to capture the first letter of each string only when the first letter is different and put it in a new NSArray.
For instance if I have an array that was like like
"a, aaa, aaaa, b, c, d, dd, ddd"
it would be like this in the new NSArray
"a, b, c, d"
Any help would be greatly appreciated.
Something like this:
- (NSArray *)indexLettersForStrings:(NSArray *)strings {
NSMutableArray *letters = [NSMutableArray array];
NSString *currentLetter = nil;
for (NSString *string in strings) {
if (string.length > 0) {
NSString *letter = [string substringToIndex:1];
if (![letter isEqualToString:currentLetter]) {
[letters addObject:letter];
currentLetter = letter;
}
}
}
return [NSArray arrayWithArray:letters];
}
NSString+LetterIndex.h
#interface NSString (LetterIndex)
#property (nonatomic, readonly) NSString * firstLetter;
#end
NSString+LetterIndex.m
#implementation NSString (LetterIndex)
- (NSString *)firstLetter
{
return self.length ? [self substringToIndex:1] : #"";
}
your method:
- (NSArray *)indexLettersForStrings:(NSArray *)strings {
NSSet * distinctValues = [NSSet setWithArray:[strings valueForKey:#"firstLetter"]];
return [[distinctValues allObjects] sortedArrayUsingSelector:#selector(compare:)]
}
also if you have some objects of custom class and want to group them by first letters of some string parameter, you can use this:
NSSet * distinctValues = [NSSet setWithArray:[objects valueForKeyPath:#"myStringParam.firstLetter"]];
NSArray *array = [NSArray arrayWithObjects:#"a", #"aaa", #"aaaa",#"b", #"c", #"d", #"dd", #"ddd", nil];
BOOL control = YES;
NSMutableArray *array2 = [NSMutableArray array];
for (int i = 0; i<array.count; i++) {
for (int j = 0; j<array2.count;j++){
if ([[array2 objectAtIndex:j]isEqualToString:[[array objectAtIndex:i]substringToIndex:1]]){
control = NO;
}
else
control = YES;
}
if (control)
[array2 addObject:[[array objectAtIndex:i]substringToIndex:1]];
}
Try this:
NSArray *arr = #[#"a", #"aaa", #"aaaa", #"b", #"c", #"d", #"dd", #"ddd"];
NSMutableArray *newArr = [NSMutableArray array];
NSMutableSet *set = [NSMutableSet set];
for (NSString *str in arr)
{
if (![set containsObject:[str substringToIndex:1]])
[newArr addObject:[str substringToIndex:1]];
[set addObject:[str substringToIndex:1]];
}
NSLog(#"%#", newArr);
This uses a Set to keep track of occurrences that already past threw. When it doesnt exist it places them into a new array.