Q: Create a comma-separated list with "and" before the last item in Objective-C, in particular I want to write a method that provides a human-readable string representation of arrays, with comma-spaces when necessary (incl. the Oxford Comma), and a conjoining " and " at the end.
For example, say I have these arrays:
NSArray *nun = @[];
NSArray *won = @[@"The Loneliest"];
NSArray *too = @[@"Peas", @"Pod"];
NSArray *tree = @[@"Apple", @"Falls", @"Far, Far Away"];

I want to write a method like:
+ (NSString*) humanReadableListFromArray: (NSArray*) arr
{
  // magic
}

And when I pass my arrays through, I want them to look like this:
@""
@"The Loneliest"
@"Peas and Pod"
@"Apple, Falls, and Far, Far Away"

Note that the first one, having exactly 1 item, is just the first item without decoration. The second, having exactly 2 items, has no commas, but does have the conjoining " and ". The third, having more than 2 items, includes a comma-space between each item, and the last item includes an additional and just after the comma-space and before the item.
Is there a short way to do this in Objective C? I've done this in languages like Java, but I know that Objective C features methods like -(NSString*)[NSArray componentsJoinedByString:] that might assist in this.

This question is not answered by Replace last comma in string with an "and". (Objective-C) because it does not address commas in the last array item. I've also looked into questions like Objective-C Simplest way to create comma separated string from an array of objects, and they don't mention this nice part of human readability.

A: That third party library @timgcarlson mentions sounds promising.  Here's what I'd do natively...
- (NSString *)humanReadableListFromArray:(NSArray *)array withOxfordStyle:(BOOL)oxford {
    if (array.count == 0) return @"";
    if (array.count == 1) return array[0];
    if (array.count == 2) return [array componentsJoinedByString:@" and "];

    NSArray *firstItems = [array subarrayWithRange:NSMakeRange(0, array.count-1)];
    NSString *lastItem = [array lastObject];
    NSString *lastDelimiter = (oxford)? @", and " : @" and ";
    return [NSString stringWithFormat:@"%@%@%@",
        [firstItems componentsJoinedByString:@", "], lastDelimiter, lastItem];
}


A: TTTFormatterKit has exactly what you need.
Here is the example from the project's README...
NSArray *list = [NSArray arrayWithObjects:@"Russel", @"Spinoza", @"Rawls", nil];
TTTArrayFormatter *arrayFormatter = [[TTTArrayFormatter alloc] init];
[arrayFormatter setUsesSerialDelimiter:NO]; // Omit Oxford Comma
NSLog(@"%@", [arrayFormatter stringFromArray:list]); // "Russell, Spinoza and Rawls"


A: Not tested:
+ (NSString*)humanReadableListFromArray:(NSArray*) arr
{
   NSUInteger count = [arr count];
   if (count == 0)
        return @"";
   else if (count == 1)
        return arr[0];

   NSString *subarrayStr;
   if (count == 2) {
       subarrayStr = arr[0];
   } else {
       NSArray *subarray = [arr subarrayWithRange:NSMakeRange(0, count - 1)];
       subarrayStr = [subarray componentsJoinedByString:@", "];
   }

   return [[subarrayStr stringByAppendingString:@" and "] stringByAppendingString:arr[count - 1]];
}


A: Here is how to do it in Swift 3:
extension Collection where Iterator.Element == String {
    func joinedWithComma() -> String {
        var string = joined(separator: ", ")

        if let lastCommaRange = string.range(of: ", ", options: .backwards) {
            string.replaceSubrange(lastCommaRange, with: " and ")
        }

        return string
    }
}

["A", "B", "C"].joinedWithComma() // returns "A, B and C"


A: Here's a Swift 4 version. It supports commas within items, the optional use of the oxford comma and a max item count.  If the number of elements is higher than maxItemCount it shortens the list and adds etc. to the end of the list.  Note: It is assumed that this function is used to add lists to the end of a sentence and so there is no punctuation after "etc", otherwise you would end up with 2 periods e.g. "I like many fruits: apples, pears, strawberries, etc.."
extension Array where Iterator.Element == String {
    func joinedWithComma(useOxfordComma: Bool = false, maxItemCount: Int = -1) -> String {
        let result: String
        if maxItemCount >= 0 && count > maxItemCount {
            result = self[0 ..< maxItemCount].joined(separator: ", ") + ", etc"
        } else if count >= 2 {
            let lastIndex = count - 1
            let extraComma = (useOxfordComma && count > 2) ? "," : ""
            result = self[0 ..< lastIndex].joined(separator: ", ") + extraComma + " and " + self[lastIndex]
        } else if count == 1 {
            result = self[0]
        } else {
            result = ""
        }

        return result
    }
}


A: As of iOS 13, there is now built-in support for this: (NS)ListFormatter, which is even better than the other answers listed here, because it handles localization.
