2D Array extension Swift 3.1.1 - arrays

I am trying to make an Array extension in Swift 3.1.1 that supports the addition of an object to a certain index in a 2D Array even if the array hasn't been populated yet. The extension should also provide the ability to get an object at certain indexPath. I have the code for this in Swift 2 but I don't seem to be able to migrate it to Swift 3. This is the Swift 2 code:
extension Array where Element: _ArrayProtocol, Element.Iterator.Element: Any {
mutating func addObject(_ anObject : Element.Iterator.Element, toSubarrayAtIndex idx : Int) {
while self.count <= idx {
let newSubArray = Element()
self.append(newSubArray)
}
var subArray = self[idx]
subArray.append(anObject)
}
func objectAtIndexPath(_ indexPath: IndexPath) -> Any {
let subArray = self[indexPath.section]
return subArray[indexPath.row] as Element.Iterator.Element
}
}
The code is taken from this answer.

As Martin says in his answer here, _ArrayProtocol is no longer public in Swift 3.1, therefore meaning that you cannot use it as a constraint in your extension.
A simple alternative in your case is to instead constrain the Array's Element to being a RangeReplaceableCollection – which both defines an init() requirement meaning "empty collection", and an append(_:) method in order to add elements to the collection.
extension Array where Element : RangeReplaceableCollection {
typealias InnerCollection = Element
typealias InnerElement = InnerCollection.Iterator.Element
mutating func fillingAppend(
_ newElement: InnerElement,
toSubCollectionAtIndex index: Index) {
if index >= count {
append(contentsOf: repeatElement(InnerCollection(), count: index + 1 - count))
}
self[index].append(newElement)
}
}
Note also that we're doing the append as a single call (using append(contentsOf:), ensuring that we only have to resize the outer array at most once.
For your method to get an element from a given IndexPath, you can just constrain the inner element type to being a Collection with an Int Index:
// could also make this an extension on Collection where the outer Index is also an Int.
extension Array where Element : Collection, Element.Index == Int {
subscript(indexPath indexPath: IndexPath) -> Element.Iterator.Element {
return self[indexPath.section][indexPath.row]
}
}
Note that I've made it a subscript rather than a method, as I feel it fits better with Array's API.
You can now simply use these extensions like so:
var arr = [[Int]]()
arr.fillingAppend(6, toSubCollectionAtIndex: 3)
print(arr) // [[], [], [], [6]]
let indexPath = IndexPath(row: 0, section: 3)
print(arr[indexPath: indexPath]) // 6
Although of course if you know the size of the outer array in advance, the fillingAppend(_:toSubCollectionAtIndex:) method is redundant, as you can just create your nested array by saying:
var arr = [[Int]](repeating: [], count: 5)
which will create an [[Int]] array containing 5 empty [Int] elements.

There's no need to limit all these ideas to the concrete Array type.
Here's my solution. This discussion was great in that I just learned about RangeReplaceableCollection. Merging (what I think is) the best of both worlds, I pushed all the operations down (up?) the Type hierarchy as far as possible.
Subscript works on much more than Array as #Hamish says. But also, there's no need to constrain the index type, so we have to get rid of IndexPath. We can always sugar this with typealias Index2d = ...
extension Collection where Self.Element: Collection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
}
}
Why not have a mutable version at the most generic possible level (between Collection and RangeReplaceableCollection) (unfortunately I don't think the getter can be inherited when we redefine subscript):
extension MutableCollection where Self.Element: MutableCollection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
set {
self[indexTuple.row][indexTuple.column] = newValue
}
}
}
Then, if you want to initialize lazily, avoid using init:repeatedValue and revise set to have auto-initialization semantics. You can trap bounds overflow and add missing empty elements in both dimensions by integrating the accepted answer's fillingAppend idea.
And when creating a 2D initializer, why not extend the idea of repeating in the natural way:
extension RangeReplaceableCollection where Element: RangeReplaceableCollection {
init(repeating repeatedVal: Element.Element, extents: (row: Int, column: Int)) {
let repeatingColumn = Element(repeating: repeatedVal, count: extents.column)
self.init(repeating: repeatingColumn, count: extents.row)
}
}
Example Usage:
enum Player {
case first
case second
}
class Model {
let playerGrid: Array<Array<Player>> = {
var p = [[Player]](repeating: .first, extents: (row: 10, column: 10))
p[(3, 4)] = .second
print("Player at 3, 4 is: \(p[(row: 3, column: 4)])")
return p
}()
}

Related

Opposite of Swift `zip` — split tuple into two arrays

I have an array of key-value pairs:
let arr = [(key:"hey", value:["ho"]), (key:"ha", value:["tee", "hee"])]
I'm splitting it into two arrays, like this:
let (keys, values) = (arr.map{$0.key}, arr.map{$0.value})
Effectively, that's the opposite of zip — I'm turning an array of tuples into two arrays.
But I don't like the fact that I'm calling map twice, because that means I'm looping through the array twice. Yet neither do I want to declare the two target arrays beforehand as empty arrays and loop once while appending, e.g. with forEach. Is there some wonderful Swifty idiom for unzipping my array of tuples into two arrays?
In Swift 4, you can use reduce(into:):
let (keys, values) = arr.reduce(into: ([String](), [[String]]())) {
$0.0.append($1.key)
$0.1.append($1.value)
}
You said:
Yet neither do I want to declare the two target arrays beforehand as empty arrays and loop once while appending, e.g. with forEach.
Personally, that's precisely what I would do. I would just write a function that does this (that way you're not sprinkling your code with that pattern). But I think the following is much more clear and intuitive than the reduce pattern, but doesn't suffer the inefficiency of the dual-map approach.
/// Unzip an `Array` of key/value tuples.
///
/// - Parameter array: `Array` of key/value tuples.
/// - Returns: A tuple with two arrays, an `Array` of keys and an `Array` of values.
func unzip<K, V>(_ array: [(key: K, value: V)]) -> ([K], [V]) {
var keys = [K]()
var values = [V]()
keys.reserveCapacity(array.count)
values.reserveCapacity(array.count)
array.forEach { key, value in
keys.append(key)
values.append(value)
}
return (keys, values)
}
Or, if you feel compelled to make it an extension, you can do that, too:
extension Array {
/// Unzip an `Array` of key/value tuples.
///
/// - Returns: A tuple with two arrays, an `Array` of keys and an `Array` of values.
func unzip<K, V>() -> ([K], [V]) where Element == (key: K, value: V) {
var keys = [K]()
var values = [V]()
keys.reserveCapacity(count)
values.reserveCapacity(count)
forEach { key, value in
keys.append(key)
values.append(value)
}
return (keys, values)
}
}
Implement this however you'd like, but when you have it in a function, you can favor clarity and intent.
Swift 4
reduce(into:) is great, but don't forget to reserveCapacity to prevent reallocation overhead:
extension Array {
func unzip<T1, T2>() -> ([T1], [T2]) where Element == (T1, T2) {
var result = ([T1](), [T2]())
result.0.reserveCapacity(self.count)
result.1.reserveCapacity(self.count)
return reduce(into: result) { acc, pair in
acc.0.append(pair.0)
acc.1.append(pair.1)
}
}
}
Prior to Swift 4
I would apply the KISS principle:
extension Array {
func unzip<T1, T2>() -> ([T1], [T2]) where Element == (T1, T2) {
var result = ([T1](), [T2]())
result.0.reserveCapacity(self.count)
result.1.reserveCapacity(self.count)
for (a, b) in self {
result.0.append(a)
result.1.append(b)
}
return result
}
}
let arr = [
(key: "hey", value: ["ho"]),
(key: "ha", value: ["tee", "hee"])
]
let unzipped = (arr as [(String, [String])]).unzip()
print(unzipped)
Not pretty but the only thing I could come up with right now: using reduce:
let (keys, values) = arr.reduce(([], [])) { ($0.0.0 + [$0.1.key], $0.0.1 + [$0.1.value]) }
Would be a lot prettier without having to specify the initial values which add a lot of noise and make the code not easily.
Generified it already looks a bit cleaner:
func unzip<K,V>(_ array : [(K,V)]) -> ([K], [V]) {
return array.reduce(([], [])) { ($0.0 + [$1.0], $0.1 + [$1.1])}
}
let (keys, values) = unzip(arr)

Remove specific object from array in swift 3

I have a problem trying to remove a specific object from an array in Swift 3. I want to remove item from an array as in the screenshot but I don't know the solution.
If you have any solutions please share with me.
Short Answer
you can find the index of object in array then remove it with index.
var array = [1, 2, 3, 4, 5, 6, 7]
var itemToRemove = 4
if let index = array.index(of: itemToRemove) {
array.remove(at: index)
}
Long Answer
if your array elements confirm to Hashable protocol you can use
array.index(of: itemToRemove)
because Swift can find the index by checking hashValue of array elements.
but if your elements doesn't confirm to Hashable protocol or you don't want find index base on hashValue then you should tell index method how to find the item. so you use index(where: ) instead which asks you to give a predicate clouser to find right element
// just a struct which doesn't confirm to Hashable
struct Item {
let value: Int
}
// item that needs to be removed from array
let itemToRemove = Item(value: 4)
// finding index using index(where:) method
if let index = array.index(where: { $0.value == itemToRemove.value }) {
// removing item
array.remove(at: index)
}
if you are using index(where:) method in lots of places you can define a predicate function and pass it to index(where:)
// predicate function for items
func itemPredicate(item: Item) -> Bool {
return item.value == itemToRemove.value
}
if let index = array.index(where: itemPredicate) {
array.remove(at: index)
}
for more info please read Apple's developer documents:
index(where:)
index(of:)
According to your code, the improvement could be like this:
if let index = arrPickerData.index(where: { $0.tag == pickerViewTag }) {
arrPickerData.remove(at: index)
//continue do: arrPickerData.append(...)
}
The index existing means Array contains the object with that Tag.
I used the solutions provided here: Remove Specific Array Element, Equal to String - Swift Ask Question
this is one of the solutions there (in case the object was a string):
myArrayOfStrings = ["Hello","Playground","World"]
myArrayOfStrings = myArrayOfStrings.filter{$0 != "Hello"}
print(myArrayOfStrings) // "[Playground, World]"

Swift: reference to array element by key

I have written my own small function to find an element in an array using a key. But I'm sure there is a ready to use implementation in Swift to get it in one line. Any hint?
func objectAtKey(array: [T], key: String) -> T? {
for element in array {
if element.name == key {
return element
}
}
return nil
}
I also know function indexOf, but this return an index, I have to use for further access. I think this is slower:
let index = array.indexOf({$0.name == key})
In Swift 3 (Xcode 8, currently beta 6) you can do
if let el = array.first(where: { $0.name == key }) {
// `el` is the first array element satisfying the condition.
// ...
} else {
// No array element satisfies the condition.
}
using the first(where:) method of the Sequence protocol:
/// Returns the first element of the sequence that satisfies the given
/// predicate or nil if no such element is found.
///
/// - Parameter predicate: A closure that takes an element of the
/// sequence as its argument and returns a Boolean value indicating
/// whether the element is a match.
/// - Returns: The first match or `nil` if there was no match.
public func first(where predicate: (Element) throws -> Bool) rethrows -> Element?
I think the best solution for you here is to use the indexOf with a Predicate that you have written. I would have written it like this though:
let array = ["Foo", "Bar", "Test"]
if let i = array.indexOf({$0 == "Foo"}) {
print(array[i])
}
To handle if the value does not exists if you need that.
Try this:
let element = array.filter{ $0.name == key }.first

How to Remove Every Other Element in an Array in Swift?

So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}

Is it possible to make an Array extension in Swift that is restricted to one class?

Can I make an Array extension that applies to, for instance, just Strings?
As of Swift 2, this can now be achieved with protocol extensions,
which provide method and property implementations to conforming types
(optionally restricted by additional constraints).
A simple example: Define a method for all types conforming
to SequenceType (such as Array) where the sequence element is a String:
extension SequenceType where Generator.Element == String {
func joined() -> String {
return "".join(self)
}
}
let a = ["foo", "bar"].joined()
print(a) // foobar
The extension method cannot be defined for struct Array directly, but only for all types
conforming to some protocol (with optional constraints). So one
has to find a protocol to which Array conforms and which provides all the necessary methods. In the above example, that is SequenceType.
Another example (a variation of How do I insert an element at the correct position into a sorted array in Swift?):
extension CollectionType where Generator.Element : Comparable, Index : RandomAccessIndexType {
typealias T = Generator.Element
func insertionIndexOf(elem: T) -> Index {
var lo = self.startIndex
var hi = self.endIndex
while lo != hi {
// mid = lo + (hi - 1 - lo)/2
let mid = lo.advancedBy(lo.distanceTo(hi.predecessor())/2)
if self[mid] < elem {
lo = mid + 1
} else if elem < self[mid] {
hi = mid
} else {
return mid // found at position `mid`
}
}
return lo // not found, would be inserted at position `lo`
}
}
let ar = [1, 3, 5, 7]
let pos = ar.insertionIndexOf(6)
print(pos) // 3
Here the method is defined as an extension to CollectionType because
subscript access to the elements is needed, and the elements are
required to be Comparable.
UPDATE: Please See Martin's answer below for Swift 2.0 updates. (I can't delete this answer since it is accepted; if Doug can accept Martin's answer, I'll delete this one to avoid future confusion.)
This has come up several times in the forums, and the answer is no, you can't do this today, but they get that it's a problem and they hope to improve this in the future. There are things they would like to add to stdlib that also need this. That's why there are so many free functions is stdlib. Most of them are work-arounds for either this problem or the "no default implementation" problem (i.e. "traits" or "mixins").
This has already been answered by the three wise-men above ;-) , but I humbly offer a generalization of #Martin's answer. We can target an arbitrary class by using "marker" protocol that is only implemented on the class that we wish to target. Ie. one does not have to find a protocol per-se, but can create a trivial one for using in targeting the desired class.
protocol TargetType {}
extension Array:TargetType {}
struct Foo {
var name:String
}
extension CollectionType where Self:TargetType, Generator.Element == Foo {
func byName() -> [Foo] { return sort { l, r in l.name < r.name } }
}
let foos:[Foo] = ["c", "b", "a"].map { s in Foo(name: s) }
print(foos.byName())
You still haven't given a use case, despite many requests in comments, so it's hard to know what you're after. But, as I've already said in a comment (and Rob has said in an answer), you won't get it literally; extensions don't work that way (at the moment).
As I said in a comment, what I would do is wrap the array in a struct. Now the struct guards and guarantees the string's type, and we have encapsulation. Here's an example, though of course you must keep in mind that you've given no indication of the kind of thing you'd really like to do, so this might not be directly satisfying:
struct StringArrayWrapper : Printable {
private var arr : [String]
var description : String { return self.arr.description }
init(_ arr:[String]) {
self.arr = arr
}
mutating func upcase() {
self.arr = self.arr.map {$0.uppercaseString}
}
}
And here's how to call it:
let pepboys = ["Manny", "Moe", "Jack"]
var saw = StringArrayWrapper(pepboys)
saw.upcase()
println(saw)
Thus we have effectively insulated our string array into a world where we can arm it with functions that apply only to string arrays. If pepboys were not a string array, we couldn't have wrapped it in a StringArrayWrapper to begin with.

Resources