Swift 2 Introspection - swift

I am converting some "old" Swift code to Swift 2.0, and I run into an error I cannot get rid of. My function receives an Array of a type (whatever), and returns a new Array of the same type.
This code no longer works in Swift 2.0:
func makePattern1<T>(var list: Array<T>) -> Array<T> {
let theType = list.dynamicType
var result = theType()
for i in 1..<list.count {
result.append(list[i])
result.append(list[i-1])
}
return result
}
giving the error message: "Initializing from a metatype value must reference 'init' explicitly".
Correcting the code with:
var result = theType.init()
gives a "Type of expression is ambiguous without more context" error.
What am I missing?

Just for fun: An alternative solution:
func makePattern2<T>(list: [T]) -> [T] {
return zip(list.dropFirst(), list).flatMap { [$0, $1] }
}
let m = makePattern2([1,2,3,4,5])
print(m) // [2, 1, 3, 2, 4, 3, 5, 4]
Remarks:
list.dropFirst() is a sequence of all but the first array elements.
zip(list.dropFirst(), list) is then a sequence of
(list[$0.i+1], list[$0.i]) tuples.
flatMap { [$0, $1] } combines these tuples to a single array again.

This code is written in Swift 2.1 and does use the map function as you requested in the comments.
func makePattern1<T>(list: [T]) -> [T] {
return list[0..<list.count-1]
.enumerate()
.map { [list[$0.index+1], list[$0.index]] }
.flatten()
.map { $0 as T }
}
Update #1
(thanks Martin R)
func makePattern1<T>(list: [T]) -> [T] {
return list[0..<list.count-1]
.enumerate()
.flatMap { [list[$0.index+1], list[$0.index]] }
}
Update #2
func makePattern1<T>(list: [T]) -> [T] {
return [Int](1..<list.count).flatMap { [list[$0], list[$0-1]] }
}
Test
makePattern1([1,2,3,4,5]) // > [2, 1, 3, 2, 4, 3, 5, 4]

Related

Overloading function in extension produces errors

I tried to add append overload for Swift Array
import Foundation
extension Array {
func append<From>(from: [From], transformer: (From) -> [Element]) {
from.forEach {
self.append(contentsOf: transformer($0))
}
}
}
And it shows me compilation error: Error:(47, 24) extraneous argument label 'contentsOf:' in call
it looks like I can't use other overloads of append in my own overload. It's really strange. Can you help?
That error is misleading. Your append function needs to be marked as mutating:
extension Array {
mutating func append<From>(from: [From], transformer: (From) -> [Element]) {
from.forEach {
self.append(contentsOf: transformer($0))
}
}
}
Alternate Implementation
(This might not be what you need, but it could help another user.)
If you make your transformer to be (From) -> Element, you can simplify this to:
extension Array {
mutating func append<From>(from: [From], transformer: (From) -> Element) {
self.append(contentsOf: from.map(transformer))
}
}
Example call:
var strings = ["a", "b", "c"]
strings.append(from: [1, 2, 3], transformer: String.init)
print(strings) // prints ["a", "b", "c", "1", "2", "3"]

compactMap result is of weird type

I expect allMembers to be of type [Member]. But instead its type is [[Member]]. Why compactMap does not return result of [Member] type?
class Team {
let members = Array(repeating: Member(), count: 2)
}
class Member {
}
let teams = Array(repeating: Team(), count: 3)
let allMembers = teams.compactMap { $0.members }
You actually need flatMap, not compactMap.
Even though previously (before Swift 4.1), compactMap was also called flatMap, it had a different implementation and function signature than the current flatMap, since compactMap can be used instead of consecutive map and filter calls to map each element to a new element while only keeping non-nil elements. On the other hand, flatMap flattens out nested lists while mapping elements.
This is the still existing flatMap on Sequence, while this is the deprecated flatMap on Sequence that was renamed to compactMap. As you can see, the function signature of the renamed version was
func flatMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
, so its closure input argument returned an Optional value (just like compactMap does now), while the still existing flatMap has a function signature
func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
, which doesn't return an Optional in its closure.
You should use the non-deprecated flatMap to flatten out your nested Array<Array<Member>>.
let allMembers = teams.flatMap { $0.members }
Adding some detail in #David's answer:
Using flatMap on a sequence (like an Array) filtering anything that maps to nil is now deprecated and replaced by compactMap.
Using flatMap on a sequence with a closure that returns an optional.
Sequence.flatMap<U>(_ transform: (Element) -> U?) -> U?
For example:
Rather than:
let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.flatMap { $0 }
You have to use:
let valid = names.compactMap { $0 }
// ["Tom", "Peter", "Harry"]
let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let counts = names.compactMap { $0?.count }
// [3, 5, 5]
But again, Swift 4.1 does not deprecate all uses of flatMap - out of 3 cases, only one case is changing. Below two situations are as it is where you need to use flatMap:
1) Using flatMap on a sequence with a closure that returns a sequence:
Sequence.flatMap<S>(_ transform: (Element) -> S) -> [S.Element] where S : Sequence
Example:
let scores = [[5,2,7], [4,8], [9,1,3]]
let allScores = scores.flatMap { $0 }
// [5, 2, 7, 4, 8, 9, 1, 3]
let passMarks = scores.flatMap { $0.filter { $0 > 5} }
// [7, 8, 9]
2) Using flatMap on an optional: The closure takes the non-nil value of the optional and returns an optional. If the original optional is nil then flatMap returns nil:
Optional.flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
Example:
let input: Int? = Int("8")
let passMark: Int? = input.flatMap { $0 > 5 ? $0 : nil}
// Optional(8)
For more detail, please refer here

Can somebody explain this change of behavior for flatMap between Swift 3 & 4? [duplicate]

I was examining .lazy for high order functions and have got some interesting compile errors related to flatMap function (and possibly others)
Examples
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
Commenting out a bit
array
.flatMap {
// print("DD")
return $0
}
.forEach {
print("SS")
print($0)
}
And everything works.. even more interesting example
array
.flatMap {
let z = $0
return $0 // Or return z - all is the same "Cannot convert return expression of type 'Int' to return type 'String?'"
}
.forEach {
print("SS")
print($0)
}
What could cause that behavior?
The flatMap(_:) method on Sequence currently (as of Swift 4) has two different meanings:
It can take a transform closure that returns an optional T?, and it will return a [T], filtering out the nil results (this overload is to be renamed to compactMap(_:) in a future version).
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult]
It can take a transform closure that returns a Sequence, and it will return an array containing the concatenation of all the resulting sequences.
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element]
Now, in Swift 4, String became a RangeReplaceableCollection (and therefore a Sequence). So Swift 3 code that did this:
// returns ["foo"], as using the `nil` filtering flatMap, the elements in the closure
// are implicitly promoted to optional strings.
["foo"].flatMap { $0 }
now does this:
// returns ["f", "o", "o"], a [Character], as using the Sequence concatenation flatMap,
// as String is now a Sequence (compiler favours this overload as it avoids the implicit
// conversion from String to String?)
["foo"].flatMap { $0 }
To preserve source compatibility, specialised flatMap overloads were added for strings:
//===----------------------------------------------------------------------===//
// The following overloads of flatMap are carefully crafted to allow the code
// like the following:
// ["hello"].flatMap { $0 }
// return an array of strings without any type context in Swift 3 mode, at the
// same time allowing the following code snippet to compile:
// [0, 1].flatMap { x in
// if String(x) == "foo" { return "bar" } else { return nil }
// }
// Note that the second overload is declared on a more specific protocol.
// See: test/stdlib/StringFlatMap.swift for tests.
extension Sequence {
#_inlineable // FIXME(sil-serialize-all)
#available(swift, obsoleted: 4)
public func flatMap(
_ transform: (Element) throws -> String
) rethrows -> [String] {
return try map(transform)
}
}
extension Collection {
#_inlineable // FIXME(sil-serialize-all)
public func flatMap(
_ transform: (Element) throws -> String?
) rethrows -> [String] {
return try _flatMap(transform)
}
}
Such that the above usage would still return a [String] in Swift 3 compatibility mode, but a [Character] in Swift 4.
So, why does
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
tell you that the closure should return a String??
Well, Swift currently doesn't infer parameter and return types for multi-statement closures (see this Q&A for more info). So the flatMap(_:) overloads where the closure returns either a generic T? or a generic S : Sequence aren't eligible to be called without explicit type annotations, as they would require type inference to satisfy the generic placeholders.
Thus, the only overload that is eligible, is the special String source compatibility one, so the compiler is expecting the closure to return a String?.
To fix this, you can explicitly annotate the return type of the closure:
array
.flatMap { i -> Int? in
print("DD")
return i
}
.forEach {
print("SS")
print($0)
}
But if you're not actually using the optional filtering functionality of this flatMap(_:) overload in your real code, you should use map(_:) instead.
flatMap can have different meanings depending on the context. You might tell the compiler more precisely which one you are intending to use.
The two usual purposes to apply flatMap to an array which the compiler can infer is to
flatten nested arrays
let array = [[1, 2, 3, 4, 5, 6], [7, 8, 9]]
let flattened = array.flatMap{$0}
print(flattened) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
map to another type and filter optionals
let array = ["1", "a", "2", "3", "b", "4", "5", "6"]
let flattened = array.flatMap{ Int($0) }
print(flattened) // [1, 2, 3, 4, 5, 6]

How to check generic class type is array?

I want to check whether the generic class type is an Array:
func test<T>() -> Wrapper<T> {
let isArray = T.self is Array<Any>
...
}
But it warns
Cast from 'T.type' to unrelated type 'Array' always fails
How can I solve this problem?
added: I've uploaded my codes to Gist.
https://gist.github.com/nallwhy/6dca541a2d1d468e0be03c97add384de
What I want to do is to parse json response according to it's an array of model or just one model.
As commentator #Holex says, you can use Any. Combine it with Mirror and you could, for example, do something like this:
func isItACollection(_ any: Any) -> [String : Any.Type]? {
let m = Mirror(reflecting: any)
switch m.displayStyle {
case .some(.collection):
print("Collection, \(m.children.count) elements \(m.subjectType)")
var types: [String: Any.Type] = [:]
for (_, t) in m.children {
types["\(type(of: t))"] = type(of: t)
}
return types
default: // Others are .Struct, .Class, .Enum
print("Not a collection")
return nil
}
}
func test(_ a: Any) -> String {
switch isItACollection(a) {
case .some(let X):
return "The argument is an array of \(X)"
default:
return "The argument is not an array"
}
}
test([1, 2, 3]) // The argument is an array of ["Int": Swift.Int]
test([1, 2, "3"]) // The argument is an array of ["Int": Swift.Int, "String": Swift.String]
test(["1", "2", "3"]) // The argument is an array of ["String": Swift.String]
test(Set<String>()) // The argument is not an array
test([1: 2, 3: 4]) // The argument is not an array
test((1, 2, 3)) // The argument is not an array
test(3) // The argument is not an array
test("3") // The argument is not an array
test(NSObject()) // The argument is not an array
test(NSArray(array:[1, 2, 3])) // The argument is an array of ["_SwiftTypePreservingNSNumber": _SwiftTypePreservingNSNumber]
Try out define an array marker protocol. Refer to this answer.
protocol AnyTypeOfArray {}
extension Array: AnyTypeOfArray {}
extension NSArray: AnyTypeOfArray {}
func isArray<T>(type: T.Type) -> Bool {
return T.self is AnyTypeOfArray.Type
}
print(isArray(type: [String].self)) // true
print(isArray(type: String.self)) // false
You are not passing any argument, so there is no type and a generic function does not make sense. Either remove the generic type:
func() {}
or, if you want to pass an argument:
let array = ["test", "test"]
func test<T>(argument: T) {
let isArray = argument is Array<Any>
print(isArray)
}
test(argument: array)
Prints: true

Swift running sum

I'd like a function runningSum on an array of numbers a (or any ordered collection of addable things) that returns an array of the same length where each element i is the sum of all elements in A up to an including i.
Examples:
runningSum([1,1,1,1,1,1]) -> [1,2,3,4,5,6]
runningSum([2,2,2,2,2,2]) -> [2,4,6,8,10,12]
runningSum([1,0,1,0,1,0]) -> [1,1,2,2,3,3]
runningSum([0,1,0,1,0,1]) -> [0,1,1,2,2,3]
I can do this with a for loop, or whatever. Is there a more functional option? It's a little like a reduce, except that it builds a result array that has all the intermediate values.
Even more general would be to have a function that takes any sequence and provides a sequence that's the running total of the input sequence.
The general combinator you're looking for is often called scan, and can be defined (like all higher-order functions on lists) in terms of reduce:
extension Array {
func scan<T>(initial: T, _ f: (T, Element) -> T) -> [T] {
return self.reduce([initial], combine: { (listSoFar: [T], next: Element) -> [T] in
// because we seeded it with a non-empty
// list, it's easy to prove inductively
// that this unwrapping can't fail
let lastElement = listSoFar.last!
return listSoFar + [f(lastElement, next)]
})
}
}
(But I would suggest that that's not a very good implementation.)
This is a very useful general function, and it's a shame that it's not included in the standard library.
You can then generate your cumulative sum by specializing the starting value and operation:
let cumSum = els.scan(0, +)
And you can omit the zero-length case rather simply:
let cumSumTail = els.scan(0, +).dropFirst()
Swift 4
The general sequence case
Citing the OP:
Even more general would be to have a function that takes any sequence
and provides a sequence that's the running total of the input
sequence.
Consider some arbitrary sequence (conforming to Sequence), say
var seq = 1... // 1, 2, 3, ... (CountablePartialRangeFrom)
To create another sequence which is the (lazy) running sum over seq, you can make use of the global sequence(state:next:) function:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) { state -> Int? in
if let val = state.it.next() {
defer { state.sum += val }
return val + state.sum
}
else { return nil }
}
// Consume and print accumulated values less than 100
while let accumulatedSum = runningSumSequence.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
// Consume and print next
print(runningSumSequence.next() ?? -1) // 120
// ...
If we'd like (for the joy of it), we could condense the closure to sequence(state:next:) above somewhat:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) {
(state: inout (sum: Int, it: AnyIterator<Int>)) -> Int? in
state.it.next().map { (state.sum + $0, state.sum += $0).0 }
}
However, type inference tends to break (still some open bugs, perhaps?) for these single-line returns of sequence(state:next:), forcing us to explicitly specify the type of state, hence the gritty ... in in the closure.
Alternatively: custom sequence accumulator
protocol Accumulatable {
static func +(lhs: Self, rhs: Self) -> Self
}
extension Int : Accumulatable {}
struct AccumulateSequence<T: Sequence>: Sequence, IteratorProtocol
where T.Element: Accumulatable {
var iterator: T.Iterator
var accumulatedValue: T.Element?
init(_ sequence: T) {
self.iterator = sequence.makeIterator()
}
mutating func next() -> T.Element? {
if let val = iterator.next() {
if accumulatedValue == nil {
accumulatedValue = val
}
else { defer { accumulatedValue = accumulatedValue! + val } }
return accumulatedValue
}
return nil
}
}
var accumulator = AccumulateSequence(1...)
// Consume and print accumulated values less than 100
while let accumulatedSum = accumulator.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
The specific array case: using reduce(into:_:)
As of Swift 4, we can use reduce(into:_:) to accumulate the running sum into an array.
let runningSum = arr
.reduce(into: []) { $0.append(($0.last ?? 0) + $1) }
// [2, 4, 6, 8, 10, 12]
By using reduce(into:_:), the [Int] accumulator will not be copied in subsequent reduce iterations; citing the Language reference:
This method is preferred over reduce(_:_:) for efficiency when the
result is a copy-on-write type, for example an Array or a
Dictionary.
See also the implementation of reduce(into:_:), noting that the accumulator is provided as an inout parameter to the supplied closure.
However, each iteration will still result in an append(_:) call on the accumulator array; amortized O(1) averaged over many invocations, but still an arguably unnecessary overhead here as we know the final size of the accumulator.
Because arrays increase their allocated capacity using an exponential
strategy, appending a single element to an array is an O(1) operation
when averaged over many calls to the append(_:) method. When an array
has additional capacity and is not sharing its storage with another
instance, appending an element is O(1). When an array needs to
reallocate storage before appending or its storage is shared with
another copy, appending is O(n), where n is the length of the array.
Thus, knowing the final size of the accumulator, we could explicitly reserve such a capacity for it using reserveCapacity(_:) (as is done e.g. for the native implementation of map(_:))
let runningSum = arr
.reduce(into: [Int]()) { (sums, element) in
if let sum = sums.last {
sums.append(sum + element)
}
else {
sums.reserveCapacity(arr.count)
sums.append(element)
}
} // [2, 4, 6, 8, 10, 12]
For the joy of it, condensed:
let runningSum = arr
.reduce(into: []) {
$0.append(($0.last ?? ($0.reserveCapacity(arr.count), 0).1) + $1)
} // [2, 4, 6, 8, 10, 12]
Swift 3: Using enumerated() for subsequent calls to reduce
Another Swift 3 alternative (with an overhead ...) is using enumerated().map in combination with reduce within each element mapping:
func runningSum(_ arr: [Int]) -> [Int] {
return arr.enumerated().map { arr.prefix($0).reduce($1, +) }
} /* thanks #Hamish for improvement! */
let arr = [2, 2, 2, 2, 2, 2]
print(runningSum(arr)) // [2, 4, 6, 8, 10, 12]
The upside is you wont have to use an array as the collector in a single reduce (instead repeatedly calling reduce).
Just for fun: The running sum as a one-liner:
let arr = [1, 2, 3, 4]
let rs = arr.map({ () -> (Int) -> Int in var s = 0; return { (s += $0, s).1 } }())
print(rs) // [1, 3, 6, 10]
It does the same as the (updated) code in JAL's answer, in particular,
no intermediate arrays are generated.
The sum variable is captured in an immediately-evaluated closure returning the transformation.
If you just want it to work for Int, you can use this:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (sums, element) in
return sums + [element + (sums.last ?? 0)]
})
}
If you want it to be generic over the element type, you have to do a lot of extra work declaring the various number types to conform to a custom protocol that provides a zero element, and (if you want it generic over both floating point and integer types) an addition operation, because Swift doesn't do that already. (A future version of Swift may fix this problem.)
Assuming an array of Ints, sounds like you can use map to manipulate the input:
let arr = [0,1,0,1,0,1]
var sum = 0
let val = arr.map { (sum += $0, sum).1 }
print(val) // "[0, 1, 1, 2, 2, 3]\n"
I'll keep working on a solution that doesn't use an external variable.
I thought I'd be cool to extend Sequence with a generic scan function as is suggested in the great first answer.
Given this extension, you can get the running sum of an array like this: [1,2,3].scan(0, +)
But you can also get other interesting things…
Running product: array.scan(1, *)
Running max: array.scan(Int.min, max)
Running min: array.scan(Int.max, min)
Because the implementation is a function on Sequence and returns a Sequence, you can chain it together with other sequence functions. It is efficient, having linear running time.
Here's the extension…
extension Sequence {
func scan<Result>(_ initialResult: Result, _ nextPartialResult: #escaping (Result, Self.Element) -> Result) -> ScanSequence<Self, Result> {
return ScanSequence(initialResult: initialResult, underlying: self, combine: nextPartialResult)
}
}
struct ScanSequence<Underlying: Sequence, Result>: Sequence {
let initialResult: Result
let underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
typealias Iterator = ScanIterator<Underlying.Iterator, Result>
func makeIterator() -> Iterator {
return ScanIterator(previousResult: initialResult, underlying: underlying.makeIterator(), combine: combine)
}
var underestimatedCount: Int {
return underlying.underestimatedCount
}
}
struct ScanIterator<Underlying: IteratorProtocol, Result>: IteratorProtocol {
var previousResult: Result
var underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
mutating func next() -> Result? {
guard let nextUnderlying = underlying.next() else {
return nil
}
previousResult = combine(previousResult, nextUnderlying)
return previousResult
}
}
One solution using reduce:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (result: [Int], item: Int) -> [Int] in
if result.isEmpty {
return [item] //first item, just take the value
}
// otherwise take the previous value and append the new item
return result + [result.last! + item]
})
}
I'm very late to this party. The other answers have good explanations. But none of them have provided the initial result, in a generic way. This implementation is useful to me.
public extension Sequence {
/// A sequence of the partial results that `reduce` would employ.
func scan<Result>(
_ initialResult: Result,
_ nextPartialResult: #escaping (Result, Element) -> Result
) -> AnySequence<Result> {
var iterator = makeIterator()
return .init(
sequence(first: initialResult) { partialResult in
iterator.next().map {
nextPartialResult(partialResult, $0)
}
}
)
}
}
extension Sequence where Element: AdditiveArithmetic & ExpressibleByIntegerLiteral {
var runningSum: AnySequence<Element> { scan(0, +).dropFirst() }
}