Swift: how can String.join() work custom types?
try this
var a = [1, 2, 3] // Ints
var s = ",".join(a.map { $0.description })
or add this extension
extension String {
func join<S : SequenceType where S.Generator.Element : Printable>(elements: S) -> String {
return self.join(map(elements){ $0.description })
}
// use this if you don't want it constrain to Printable
//func join<S : SequenceType>(elements: S) -> String {
// return self.join(map(elements){ "\($0)" })
//}
}
var a = [1, 2, 3] // Ints
var s = ",".join(a) // works with new overload of join
join is defined as
extension String {
func join<S : SequenceType where String == String>(elements: S) -> String
}
which means it takes a sequence of string, you can't pass a sequence of int to it.
join an array of custom Objects
you have tu use map
function.
var t1 = Team(id: 1, name: "Adria", shortname: "Ad")
var t2 = Team(id: 2, name: "Roger", shortname: "Ro")
var t3 = Team(id: 3, name: "Raquel", shortname: "Ra")
var array: [Team] = [t1, t2, t3];
var arrayMap: Array = array.map(){ toString($0.id) }
var joinedString: String = ",".join(arrayMap)
println(joinedString) // 1,2,3
Join string array with separator , and add , and to join the last element in Swift
Add a condition to check if your String
collection has less than or is equal to 2 elements, if true just return the two elements joined by " and "
otherwise drop the last element of your collection, join the elements with a separator ", "
then re add the last element with the final separator ", and "
.
You can extend BidirectionalCollection
protocol constraining its elements to the StringProtocol
:
Bidirectional collections offer traversal backward from any valid
index, not including a collection’s startIndex. Bidirectional
collections can therefore offer additional operations, such as a last
property that provides efficient access to the last element and a
reversed() method that presents the elements in reverse order.
Xcode 11.4 • Swift 5.2 or later
extension BidirectionalCollection where Element: StringProtocol {
var sentence: String {
count <= 2 ?
joined(separator: " and ") :
dropLast().joined(separator: ", ") + ", and " + last!
}
}
let elements = ["a", "b", "c"]
let sentenceFromElements = elements.sentence // "a, b, and c"
edit/update
Xcode 13+ • iOS15+
You can use the new generic structure ListFormatStyle
with the new instance methods of Sequence called formatted:
let elements = ["a", "b", "c"]
let formatted = elements.formatted() // "a, b, and c"
let names = ["Steve Jobs", "Wozniak", "Tim Cook", "Jony Ive"]
let formatted2and = names.formatted(.list(type: .and, width: .short)) // "Steve Jobs, Wozniak, Tim Cook, & Jony Ive"
let formatted2or = names.formatted(.list(type: .or, width: .short)) // "Steve Jobs, Wozniak, Tim Cook, or Jony Ive"
If you need a specific locale (fixed) like Portuguese Brasil:
let localeBR = Locale(identifier: "pt_BR")
let formatted2e = names.formatted(
.list(type: .and, width: .short)
.locale(localeBR)
) // "Steve Jobs, Wozniak, Tim Cook e Jony Ive"
let formatted2ou = names.formatted(
.list(type: .or, width: .short)
.locale(localeBR)
) // "Steve Jobs, Wozniak, Tim Cook ou Jony Ive"
How do I convert a Swift Array to a String?
If the array contains strings, you can use the String
's join
method:
var array = ["1", "2", "3"]
let stringRepresentation = "-".join(array) // "1-2-3"
In Swift 2:
var array = ["1", "2", "3"]
let stringRepresentation = array.joinWithSeparator("-") // "1-2-3"
This can be useful if you want to use a specific separator (hypen, blank, comma, etc).
Otherwise you can simply use the description
property, which returns a string representation of the array:
let stringRepresentation = [1, 2, 3].description // "[1, 2, 3]"
Hint: any object implementing the Printable
protocol has a description
property. If you adopt that protocol in your own classes/structs, you make them print friendly as well
In Swift 3
join
becomesjoined
, example[nil, "1", "2"].flatMap({$0}).joined()
joinWithSeparator
becomesjoined(separator:)
(only available to Array of Strings)
In Swift 4
var array = ["1", "2", "3"]
array.joined(separator:"-")
How add separator to string at every N characters in swift?
Swift 5.2 • Xcode 11.4 or later
extension Collection {
func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { start in
guard start < endIndex else { return nil }
let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
defer { start = end }
return self[start..<end]
}
}
func every(n: Int) -> UnfoldSequence<Element,Index> {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { let _ = formIndex(&index, offsetBy: n, limitedBy: endIndex) }
return self[index]
}
}
var pairs: [SubSequence] { .init(unfoldSubSequences(limitedTo: 2)) }
}
extension StringProtocol where Self: RangeReplaceableCollection {
mutating func insert<S: StringProtocol>(separator: S, every n: Int) {
for index in indices.every(n: n).dropFirst().reversed() {
insert(contentsOf: separator, at: index)
}
}
func inserting<S: StringProtocol>(separator: S, every n: Int) -> Self {
.init(unfoldSubSequences(limitedTo: n).joined(separator: separator))
}
}
Testing
let str = "112312451"
let final0 = str.unfoldSubSequences(limitedTo: 2).joined(separator: ":")
print(final0) // "11:23:12:45:1"
let final1 = str.pairs.joined(separator: ":")
print(final1) // "11:23:12:45:1"
let final2 = str.inserting(separator: ":", every: 2)
print(final2) // "11:23:12:45:1\n"
var str2 = "112312451"
str2.insert(separator: ":", every: 2)
print(str2) // "11:23:12:45:1\n"
var str3 = "112312451"
str3.insert(separator: ":", every: 3)
print(str3) // "112:312:451\n"
var str4 = "112312451"
str4.insert(separator: ":", every: 4)
print(str4) // "1123:1245:1\n"
Swift 3: Getting error while joining the reversed() array
The problem is that the joined
function requires that the separator be the same data type as the values being joined. Since self.array
is an array of T
, you can't pass a String
as the separator. This mismatch is causing the error.
Since this is your description
property and your goal is to get a string representation of the array, one solution would be to map the array of T
to an array of String
. Then you can join that String
array with your newline separator.
One way to convert something to a String
is to call the description
method on it. But to do that, you need to limit your T
to only those types that provide the description
method which comes from CustomStringConvertible
.
Change your class declaration to:
class Stack<T:CustomStringConvertible>: CustomStringConvertible {
and then your description
property to:
var description: String {
let topDivider = "### STACK ###\n"
let bottomDivider = "\n############\n"
let stackElements = self.array.reversed().map { $0.description }.joined(separator: "\n")
return topDivider + stackElements + bottomDivider
}
How can I convert an Int array into a String? array in Swift
Airspeed Velocity gave you the answer:
var arr: [Int] = [1,2,3,4,5]
var stringArray = arr.map { String($0) }
Or if you want your stringArray to be of type [String?]
var stringArray = arr.map { Optional(String($0)) }
This form of the map statement is a method on the Array type. It performs the closure you provide on every element in the array, and assembles the results of all those calls into a new array. It maps one array into a result array.
The closure you pass in should return an object of the type of the objects in your output array.
We could write it in longer form:
var stringArray = arr.map {
(number: Int) -> String in
return String(number)
}
EDIT:
If you just need to install your int values into custom table view cells, you probably should leave the array as ints and just install the values into your cells in your cellForRowAtIndexPath method.
func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell",
forIndexPath: indexPath) as! MyCustomCellType
cell.textLabel?.text = "\(arr[indexPath.row])"
return cell
}
Edit #2:
If all you want to to is print the array, you'd be better off leaving it as an array of Int objects, and simply printing them:
arr.forEach { print($0) }
Merge objects of the same type
Ultimately, the most efficient way in the fewest lines of code is probably exactly what you'd expect:
extension Coin {
func merge(with: Coin) -> Coin {
var new = Coin()
new.value = value ?? with.value
new.country = country ?? with.country
new.color = color ?? with.color
return new
}
}
let coinC = coinA.merge(with: coinB)
Note that in the above scenario, the resulting value will always be coinA
's, and will only be coinB
's if coinA
's value for a given key is nil
. Whenever you change, add, or delete a property on Coin
, you'll have to update this method, too. However, if you care more about future-proofing against property changes and don't care as much about writing more code and juggling data around into different types, you could have some fun with Codable
:
struct Coin: Codable {
var value: Float?
var country: String?
var color: String?
func merge(with: Coin, uniquingKeysWith conflictResolver: (Any, Any) throws -> Any) throws -> Coin {
let encoder = JSONEncoder()
let selfData = try encoder.encode(self)
let withData = try encoder.encode(with)
var selfDict = try JSONSerialization.jsonObject(with: selfData) as! [String: Any]
let withDict = try JSONSerialization.jsonObject(with: withData) as! [String: Any]
try selfDict.merge(withDict, uniquingKeysWith: conflictResolver)
let final = try JSONSerialization.data(withJSONObject: selfDict)
return try JSONDecoder().decode(Coin.self, from: final)
}
}
With that solution, you can call merge
on your struct
like you would any dictionary, though note that it returns a new instance of Coin
instead of mutating the current one:
let coinC = try coinA.merge(with: coinB) { (_, b) in b }
Related Topics
How to Check If a String Contains Letters in Swift
Nspredicate with Swift and Core Data
Avoid Consecutive "If Let" Declarations in Swift
Dispatchqueue:Cannot Be Called with Ascopy = No on Non-Main Thread
Reading from the Clipboard with Swift 3 on MACos
Check for Launching from Uilocalnotification in Swift
Can/How to Replace My Kvo Stuff with Rc3
Create PDF of Dynamic Size with Typography Using Uiview Template(S)
Understanding @Binding in Swiftui
Why Avplayer Downloading First Instead Live Streaming
How to Avoid Using Anypublisher/Erasetoanypublisher All Over the Place
"Missing Required Entitlement" for Nfctagreadersession
Button Border with Transparent Background in Swift
Changing Tab Bar Font in Swift
Xcode 11 Doesn't Recognize Core Data Entity
Swift: Force Show Navigation Bar in Modal
React Native Sending Events to JavaScript in Swift
Swift Subtle Difference Between Curried and Higher Order Function