5
\$\begingroup\$

Imagine you had to join together attributed strings with a separator in between them. Which of the following methods would you use?

An extension on SequenceType with a function that takes in a separator. Can't use any optionals in the array.

extension SequenceType where Generator.Element: NSAttributedString {
 func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
 var shouldAddSeparator = true
 return self.reduce(NSMutableAttributedString()) {(element, sequence) in
 if shouldAddSeparator {
 shouldAddSeparator = false
 }
 else {
 element.appendAttributedString(separator)
 }
 element.appendAttributedString(sequence)
 return element
 }
 }
}

A private function that takes in an array of optional NSAttributedString and a separator.

private func join(attributedStrings strings: [NSAttributedString?], withSeparator separator: NSAttributedString) -> NSAttributedString? {
 let unwrappedStrings = strings.flatMap{0ドル} as [NSAttributedString]
 guard unwrappedStrings.count == strings.count else { return nil }
 let finalString = NSMutableAttributedString()
 for (index, string) in unwrappedStrings.enumerate() {
 if index == 0 {
 finalString.appendAttributedString(string)
 }
 else {
 finalString.appendAttributedString(separator)
 finalString.appendAttributedString(string)
 }
 }
 return finalString
}

Extension on _ArrayType that can take in an array of options NSAttributedString

extension _ArrayType where Generator.Element == NSAttributedString? {
 private func join(withSeparator separator: NSAttributedString) -> NSAttributedString? {
 let unwrappedStrings = flatMap{0ドル} as [NSAttributedString]
 var shouldAddSeparator = false
 return unwrappedStrings.reduce(NSMutableAttributedString(), combine: { (string, element) in
 if shouldAddSeparator {
 string.appendAttributedString(separator)
 }
 else {
 shouldAddSeparator = true
 }
 string.appendAttributedString(element)
 return string
 })
 }
}
Martin R
24.2k2 gold badges37 silver badges95 bronze badges
asked Jul 25, 2016 at 3:28
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

The second and third approach combine two tasks into one:

  • Extract the non-nil elements from the given array of optional attributed strings, and
  • concatenate these with a given separator.

My suggestion is to separate these concerns. This rules out #2 and #3 and leaves us with your #1 approach, the extension method on SequenceType.

Another disadvantage of the extension _ArrayType is that it uses an internal type, so that might break in a future version of Swift.

The flatMap() method from the Swift standard library already provides a method for the first task:

let attributedStrings = optionalAttributedStrings.flatMap { 0ドル }

and this can be combined with your extension method by the caller:

let joined = optionalAttributedStrings.flatMap { 0ドル }.join(withSeparator: separator)

A user might also want to concatenate non-optional strings, which works with the extension method:

let joined = attributedStrings.join(withSeparator: separator)

but not with the other two approaches.

Another argument for approach #1 is that it resembles the existing method to join strings:

extension SequenceType where Generator.Element == String {
 public func joinWithSeparator(separator: String) -> String
}

The method itself can be improved. The reduce() method creates a new NSMutableAttributedString in each iteration. Better create only one and append all elements (similar as on your join() function):

extension SequenceType where Generator.Element: NSAttributedString {
 func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
 let finalString = NSMutableAttributedString()
 for (index, string) in enumerate() {
 if index > 0 {
 finalString.appendAttributedString(separator)
 }
 finalString.appendAttributedString(string)
 }
 return finalString
 }
}

Update: The Swift language changes constantly. For the convenience of future readers, here is an update to Swift 4 of the above code:

extension Sequence where Element: NSAttributedString {
 func join(withSeparator separator: NSAttributedString) -> NSAttributedString {
 let finalString = NSMutableAttributedString()
 for (index, string) in enumerated() {
 if index > 0 {
 finalString.append(separator)
 }
 finalString.append(string)
 }
 return finalString
 }
}

Also flatMap has been renamed to compactMap:

let attributedStrings = optionalAttributedStrings.compactMap { 0ドル }
answered Aug 2, 2016 at 19:36
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.