I just want to convert an array into a tuple in Swift; something like the following:
>>> myArray = [1,2,3,4,5]
>>> mytuple = tuple(myArray)
>>> mytuple
(1, 2, 3, 4, 5)
What's the easiest way of doing that?
(I only started learning Swift today, so I am very, very new).
It's actually quite possible, if you are willing to do some low level magic. I don't think it works if the tuple has a collection type, though. This is mainly for interacting with C libraries.
typealias TupleType = (UInt8, UInt8, UInt8)
var array = [2, 3, 4] as [UInt8]
var tuple = UnsafeMutablePointer<StepsType>(malloc(UInt(sizeof(TupleType))))
memcpy(tuple, array, UInt(array.count))
More of this stuff can be found here:
https://codereview.stackexchange.com/questions/84476/array-to-tuple-in-swift/84528#84528
Because 70% of us are highly visual beings:
You can't do this because the size of a tuple is part of its type, and this isn't true for an array. If you know the array's length, you can just do let myTuple = (myArray[0], myArray[1], ...). Or you can build your architecture around tuples from the beginning. What are you trying to do?
This is a common problem which occurs in lots of other languages. See How do I convert a list to a tuple in Haskell?
if what you want to do is extract multiple value from array at the same time, maybe
the following code could help you
extension Array {
func splat() -> (Element,Element) {
return (self[0],self[1])
}
func splat() -> (Element,Element,Element) {
return (self[0],self[1],self[2])
}
func splat() -> (Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3])
}
func splat() -> (Element,Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3],self[4])
}
}
then you can use it like this
let (first,_,third) = ( 0..<20 ).map { $0 }.splat()
you can even write a codegen to generate the extension code
This my universal solution for copy array data to tuple. The only problem that I could not solve is to check the type of the element in the tuple and the element from the array.
/// Unsafe copy array to tuple
func unsafeCopyToTuple<ElementType, Tuple>(array: inout Array<ElementType>, to tuple: inout Tuple) {
withUnsafeMutablePointer(to: &tuple) { pointer in
let bound = pointer.withMemoryRebound(to: ElementType.self, capacity: array.count) { $0 }
array.enumerated().forEach { (bound + $0.offset).pointee = $0.element }
}
}
Scared of unsafe pointers? Or need support for tuples with different types inside?
It can also be done like this:
public extension Array {
public var tuple: Any? {
switch count {
case 0:
return ()
case 1:
return (self[0])
case 2:
return (self[0], self[1])
case 3:
return (self[0], self[1], self[2])
case 4:
return (self[0], self[1], self[2], self[3])
case 5:
return (self[0], self[1], self[2], self[3], self[4])
default:
return nil
}
}
Because typing this out can be a little error prone, it's a good idea to write tests for this that make sure the right tuple is returned.
I've used this approach in ArrayPlusTuple.
So instead of writing this functionality yourself you could just add pod 'ArrayPlusTuple' to your pod file and get this tested functionality for free.
Related
I am taking some inspiration from
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.
I would like the implementation to be as type safe as possible, however should be reusable.
The strategy that I am using is declaring a Weak Reference container as so.
class WeakRefContainer<T> where T: AnyObject {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
Then I want to maintain an array of these WeakRefContainers, so I create an array extension:
extension Array where Element: WeakRefContainer<AnyObject> {
func compact() -> [WeakRefContainer<AnyObject>] {
return filter { $0.value != nil }
}
}
When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.
I am now having some compilation issues which am having trouble understanding.
Lets suppose I have a sample class
class SampleClass {
}
And I try to use everything as follows:
var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)
weakReferencesArray.compact()
When I try to call compact I get the following error message:
MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'
Can anyone unblock me please? Thanks
Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.
There is a workaround for this, via a protocol:
protocol WeakHolder {
var hasRef: Bool { get }
}
extension WeakRefContainer: WeakHolder {
var hasRef: Bool { return value != nil }
}
extension Array where Element: WeakHolder {
func compacted() -> [Element] {
return filter { $0.hasRef }
}
mutating func compact() {
self = compacted()
}
}
I also renamed compact to compacted, for better Swift semantics, and replaced the original compact by a mutating version.
You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject.
extension Array where Element: WeakRefContainer<T> {
However, currently, parameterised extensions are not possible. See this proposal.
You can kind of work around this by making compact generic:
extension Array{
func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
return filter { $0.value != nil }
}
}
I'm attempting to create a FIFO array in swift. I want to create something that acts like this:
var Arr = FixedFIFOArray<Int>(maxSize:3)
Arr.append(1) //Arr = [1]
Arr.append(2) //Arr = [1,2]
Arr.append(3) //Arr = [1,2,3]
Arr.append(4) //Arr = [2,3,4] <- the max size is fixed to 3, so any
additional values added remove old values
Other than this behavior, it should act like an array: allow slicing, indexing, iterating in for loops, etc.
In any other language, this would be a job for subclassing. We aren't changing much, just adding an initializer and amending a function or two. However, in Swift, we can't subclass array. What is the best way to do this? Do I need to implement every protocol that array implements, and just pass the associated functions off to an array? Something like this:
struct FixedFIFOArray<T> {
var _maxSize: Int
var _array: [T] = []
init(maxSize: Int) {
self._maxSize = maxSize
}
}
extension FixedFIFOArray : Collection {
//...
}
extension FixedFIFOArray : RandomAccessCollection {
//...
}
extension FixedFIFOArray : Sequence {
//...
}
// etc...
This seems like a lot of work to do something so simple. What am I missing?
It is not as bad as it seems, because many protocol requirements have
default implementations.
Unfortunately I do not have the perfect recipe to find a "minimal" implementation.
Some information can be found in the
RandomAccessCollection documentation
where some methods are marked as "Required. Default implementation provided."
You can also start with an empty implementation extension FixedFIFOArray : RandomAccessCollection {} and study the error messages or try the Fix-its.
With "Jump to Definiton" in the Xcode editor one can inspect the protocol definitions and extension methods.
In your case it turned out that it suffices to implement
startIndex, endIndex, and subscript:
extension FixedFIFOArray : RandomAccessCollection {
var startIndex: Int {
return _array.startIndex
}
var endIndex: Int {
return _array.endIndex
}
subscript(i: Int) -> T {
return _array[i]
}
}
Or, if you need a read-write subscript:
subscript(i: Int) -> T {
get {
return _array[i]
}
set {
_array[i] = newValue
}
}
I wrote an extension to Array that allows me to pop the last element and instantly add it to another array:
extension Array {
mutating func popLast(to otherArray: inout [Element]) -> Element? {
guard self.count > 0 else { return nil }
return otherArray.appendAndReturn(self.popLast()!)
}
mutating func appendAndReturn(_ element: Element) -> Element {
self.append(element)
return element
}
}
This simple example in playground works like a charm:
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
newNumbers.popLast(to: &usedNumbers)
print(usedNumbers) // [9]
for _ in newNumbers {
newNumbers.popLast(to: &usedNumbers)
}
print(usedNumbers) // [9, 8, 7, 6, 5, 4, 3, 2, 1]
But using the extension inside of a struct (code after the warning) gives me this warning:
Simultaneous accesses to parameter 'self', but modification requires
exclusive access; consider copying to a local variable
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
return newNumbers.popLast(to: &usedNumbers)
}
}
It is only a warning and my app runs just fine with the expected behavior, but I'm curious if there really is a danger here. Looking at SE-0176, I understand the goal of the warning, if I were to use it to pop the last element from the same array I append it to, because copy-on-write could mess that up. And so I guess it's related to the struct. But using it on two different arrays inside the same struct, I see no danger. Am I missing something and is there a way to write the extension that would circumvent the potential problem?
Update:
Your code now works without modification.
As #Hamish noted in the comments below, this was a bug that is now fixed in the version of Swift which shipped with Xcode 9 beta 3. I also verified that it works at the IBM Swift Sandbox which is using Linux x86_64 build Swift Dev. 4.0 (Jul 13, 2017).
As you figured out in the comments, the problem is that you need exclusive access to the struct in order to mutate it, but you're passing a reference to part of the struct to the inout parameter. This is apparently supposed to work since you're accessing different parts of the structure, but due to a bug the compiler is too strict here.
The warning suggests copying to a local variable. Since your return is complicated, I have used a defer statement to return the copy of newNumbers to newNumbers to avoid having to store the result of the call in a temporary variable:
struct Test {
var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []
mutating func getNewNumber() -> Int? {
var newNumbersCopy = newNumbers
defer { newNumbers = newNumbersCopy }
return newNumbersCopy.popLast(to: &usedNumbers)
}
}
You could also choose to fix it by making a copy of the usedNumbers:
mutating func getNewNumber() -> Int? {
var usedNumbersCopy = usedNumbers
defer { usedNumbers = usedNumbersCopy }
return newNumbers.popLast(to: &usedNumbersCopy)
}
I need A functional Approach to this situation: I have two arrays of the same Object Type and I need create a function that returns a tuple array of these objects with the same ID.
Here my imperative implementation:
func filterById(personArray : [Person], anotherPersonArray : [Person]) -> [(Person,Person)]{
var ret = [(Person,Person)]()
for person in personArray{
for person2 in anotherPersonArray{
if person.id?.intValue == person2.id?.intValue{
ret.append(person,person2)
}
}
}
return ret
And now my functional Attempt to achieve this, but it always returns an Empty Array
return Array(zip(personArray,anotherPersonArray)).filter
{$0.id?.intValue == $1.id?.intValue}}}
How can I do a Functional Approach to this?
You can basically do what #toskv said in his comment: cartesian product and then filter by the same id's. You can do it like this:
func filterById(a : [Person], b : [Person]) -> [(Person, Person)] {
let lazyCartProd = a.lazy.map{ p in
b.lazy.map{ (p, $0) }
}.flatten()
let lazySame = lazyCartProd.filter{ $0.id == $1.id }
return Array(lazySame)
}
Note that there is no intermediate array created because it's lazy. You could also pack this into one statement, but I didn't because of clarity.
Fun thing: lazySame is of type:
LazyFilterCollection<FlattenBidirectionalCollection<LazyMapCollection<Array<Person>, LazyMapCollection<Array<Person>, (Person, Person)>>>>
That's why type inference is cool :D
Probably you shouldn't use array's at all though, if you make your Person type conform to Hashable, you can put it in a Set and use the much more efficient methods like intersect, contains, union, etc. whose complexity is about n times better than if you'd use an array.
Try this:
return personArray.map {
p -> [(Person, Person)] in
anotherPersonArray
.filter { $0.id == p.id }
.map { (p, $0) }
}.flatMap { $0 }
I assume that id is unique within each array. SwiftStub.
Consider the following silly, simple example:
let arr = ["hey", "ho"]
let doubled = arr.map {$0 + $0}
let capitalized = arr.map {$0.capitalizedString}
As you can see, I'm processing the same initial array in multiple ways in order to end up with multiple processed arrays.
Now imagine that arr is very long and that I have many such processes generating many final arrays. I don't like the above code because we are looping multiple times, once for each map call. I'd prefer to loop just once.
Now, obviously we could handle this by brute force, i.e. by starting with multiple mutable arrays and writing into all of them on each iteration:
let arr = ["hey", "ho"]
var doubled = [String]()
var capitalized = [String]()
for s in arr {
doubled.append(s + s)
capitalized.append(s.capitalizedString)
}
Fine. But now we don't get the joy of using map. So my question is: is there a better, Swiftier way? In a hazy way I imagine myself using map, or something like map, to generate something like a tuple and magically splitting that tuple out into all resulting arrays as we iterate, as if I could say something like this (pseudocode, don't try this at home):
let arr = ["hey", "ho"]
let (doubled, capitalized) = arr.map { /* ???? */ }
If I were designing my own language, I might even permit a kind of splatting by assignment into a pseudo-array of lvalues:
let arr = ["hey", "ho"]
let [doubled, capitalized] = arr.map { /* ???? */ }
No big deal if it can't be done, but it would be fun to be able to talk this way.
How about a function, multimap, that takes a collection of transformations, and applies each one, returning them as an array of arrays:
// yay protocol extensions
extension SequenceType {
// looks like T->U works OK as a constraint
func multimap
<U, C: CollectionType
where C.Generator.Element == Generator.Element->U>
(transformations: C) -> [[U]] {
return transformations.map {
self.map($0)
}
}
}
Then use it like this:
let arr = ["hey", "ho"]
let double: String->String = { $0 + $0 }
let uppercase: String->String = { $0.uppercaseString }
arr.multimap([double, uppercase])
// returns [["heyhey", "hoho"], ["HEY", "HO"]]
Or it might be quite nice in variadic form:
extension SequenceType {
func multimap<U>(transformations: (Generator.Element->U)...) -> [[U]] {
return self.multimap(transformations)
}
}
arr.multimap({ $0 + $0 }, { $0.uppercaseString })
Edit: if you want separate variables, I think the best you can do is a destructure function (which you have to declare n times for each n-tuple unfortunately):
// I don't think this can't be expressed as a protocol extension quite yet
func destructure<C: CollectionType>(source: C) -> (C.Generator.Element,C.Generator.Element) {
precondition(source.count == 2)
return (source[source.startIndex],source[source.startIndex.successor()])
}
// and, since it's a function, let's declare pipe forward
// to make it easier to call
infix operator |> { }
func |> <T,U>(lhs: T, rhs: T->U) -> U {
return rhs(lhs)
}
And then you can declare the variables like this:
let (doubled,uppercased)
= arr.multimap({ $0 + $0 }, { $0.uppercaseString }) |> destructure
Yes this is a teensy bit inefficient because you have to build the array then rip it apart – but that’s really not going to be material, since the arrays are copy-on-write and we’re talking about a small number of them in the outer array.
edit: an excuse to use the new guard statement:
func destructure<C: Sliceable where C.SubSlice.Generator.Element == C.Generator.Element>(source: C) -> (C.Generator.Element,C.Generator.Element) {
guard let one = source.first else { fatalError("empty source") }
guard let two = dropFirst(source).first else { fatalError("insufficient elements") }
return (one,two)
}
What is wrong with your suggestion of tuple?
let arr = ["hey", "ho"]
let mapped = arr.map {e in
return (e + e, e.capitalizedString)
}
How about this, we process 'capitalized' array while we map the 'doubled' array:
let arr = ["hey", "ho"]
var capitalized = [String]()
let doubled = arr.map {(var myString) -> String in
capitalized.append(myString.capitalizedString)
return myString + myString
}
//doubled ["heyhey", "hoho"]
//capitalized: ["Hey", "Ho"]