Swift: reference to array element by key - arrays

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

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)

Check whether integers in array are consecutive or in sequence

Is there any best method to check if elements in array are in consecutive order?
Eg:
[1,2,3,4,5] // returns true
[1,2,4,3,5] // returns false
Currently what I implement is to take difference of elements and if the diff is 1 then I say it is in consecutive order.
I'm looking for any improved approach. I think of adding extension to Array but not sure how to implement this.
Given your array
let list = [1,2,3,4,5]
you can use some Functional Programming magic
let consecutives = list.map { $0 - 1 }.dropFirst() == list.dropLast()
If this is a one-off question, then any little for-loop is fine, but it's an interesting problem to explore generic solutions. First, I'm assuming you mean that each element must be one greater than the one before, not just in order.
Let's build a generic way to answer "do all pairs of elements in this collection obey some rule." First, it'd be really nice to have a generic way to say "do all?"
extension Sequence {
func all(pass predicate: (Element) -> Bool) -> Bool {
// If nothing is false, everything is true
return !self.contains(where: { !predicate($0) })
}
}
This returns whether all elements of a sequence obey some rule.
Now we can ask the question: do all pair-wise elements of a collection obey some rule:
extension Collection {
func passesForConsecutiveValues(_ predicate:(Element, Element) -> Bool) -> Bool {
return zip(self, dropFirst()).all(pass: predicate)
}
}
zip(x, x.dropFirst() just creates "pair-wise elements", and then we ask "do they all satisfy our rule?" For example:
// Are all elements one more than their predecessor?
[1,2,4,5].passesForConsecutiveValues { $1 == $0 + 1 } // true
Now you may have noticed that I switched from Sequence to Collection in the middle there. Why? Because zip(x, x.dropFirst()) isn't defined on arbitrary sequences. You may only be allowed to iterate over a sequence once. Unfortunately there's no way to know; it's considered "special knowledge about the sequence" in the docs. Bleh. I miss Scala's TraversableOnce vs. Sequence that moves the requirement into the type.
That said, we absolutely can build this for Sequence. We just have to build a replacement for zip(x, x.dropFirst()). We'll call it pairwise and it'll return an iterator:
extension Sequence {
func pairwise() -> AnyIterator<(Element, Element)> {
var it = makeIterator()
guard var last_value = it.next() else { return AnyIterator{ return nil } }
return AnyIterator {
guard let value = it.next() else { return nil }
defer { last_value = value }
return (last_value, value)
}
}
}
And with that, we can build this on Sequence:
extension Sequence {
func passesForConsecutiveValues(_ predicate:(Element, Element) -> Bool) -> Bool {
return pairwise().all(pass: predicate)
}
}
"Currently what I implement is to take difference of elements and if
the diff is 1 then I say it is in sequence."
Based on your statement above, it seems your want to, for an array of integers, see if all members are consecutive.
You've already described the logic for this algorithm: you could implement it e.g. using a for ... in ... where loop, with a body which is only ever entered when the where clause identifies two subsequent elements which are not in consecutive order. E.g.:
extension Array where Element == Int {
func numbersAreConsecutive() -> Bool {
for (num, nextNum) in zip(self, dropFirst())
where (nextNum - num) != 1 { return false }
return true
}
}
var arr = [1, 2, 3, 4, 5]
print(arr.numbersAreConsecutive()) // true
arr = [1, 2, 4, 5]
print(arr.numbersAreConsecutive()) // false
arr = [1]
print(arr.numbersAreConsecutive()) // true
arr = []
print(arr.numbersAreConsecutive()) // true
arr = [2, 1]
print(arr.numbersAreConsecutive()) // false
Extending the extension to all types conforming to Integer:
extension Array where Element: Integer {
func numbersAreConsecutive() -> Bool {
for (num, nextNum) in zip(self, dropFirst())
where (nextNum - num) != 1 { return false }
return true
}
}
It will return the true if the sequence is expected otherwise it will return the false
It has two check
1.Checking whether the array is sequence(Find the array is sequence)
1.1 Sortedarray[0] + arraycount multiple with sequence (1,2,3, etc) and minus the sequence.
1.2 compare the above calculated value with last value of sorted array. if it matche we could consider The array is sequence.
2. Compare the source array and sorted array to confirm it is in order
isSeq([4,5,6,7],sequence:1) **return True**
isSeq([100,102,104,106,108],sequence:2) **return True**
isSeq([100,103,106,109,110],sequence:3) **return false**
func isSeq(_ arrayValue:[Int],sequence:Int) ->Bool{
let sortedValue = arrayValue.sorted()
if(sortedValue[0] + (sortedValue.count * sequence) - sequence == sortedValue[sortedValue.count - 1]){
if(arrayValue == sortedValue){
return true
}
}
return false;
}

2D Array extension Swift 3.1.1

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
}()
}

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 indexOf inconsistent behavior [duplicate]

This question already has answers here:
NSObject subclass in Swift: hash vs hashValue, isEqual vs ==
(4 answers)
Closed 6 years ago.
It seems that I've facing inconsistent behavior with Swift's indexOf function.
There are two indexOf functions in Swift:
1. The first one takes Equatable as an argument: array.indexOf(obj)
2. The second takes matching closure as an argument: array.indexOf{$0 == obj}
I have an NSObject subclass for which == operator is defined, i.e. it conforms to Equatable, therefore, I assume both function to work exactly the same (with closure in example).
However, the 1st method behaves inconsistently, in particular it returns nil when being invoked with object, instance of which is not present in array.
To illustrate issue, I provide example code with comments:
class EqObj: NSObject {
let value: Int
init(value: Int) {
self.value = value
}
}
func ==(lhs: EqObj, rhs: EqObj) -> Bool{
return lhs.value == rhs.value
}
var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)
// returns nil, should return 2 - incorrect
array.indexOf(object) // Instance is not present
// returns 2, correct
array.indexOf(array.last!) // Instance is present
// returns 2, correct
array.indexOf{$0 == object} // Instance is not present, predicate
// returns non-empty array, correct
array.filter{$0 == object} // Instance is not present, predicate
The issue is reproducible only with NSObject subclasses. When I change Obj: NSObject to Obj: Equatable method indexOf() works exactly as expected, i.e. returns 2.
The question is whether this can be considered a bug?
My assumption is that array.indexOf(object) invokes isEqual method of NSObject and not my overloaded == operator.
My solution:
I use array.indexOf{$0 == object}
First of all with this definition I am getting different results
class Obj: NSObject {
let value: Int
init(value:Int) {
self.value = value
}
}
func ==(lhs: Obj, rhs: Obj) -> Bool{
return lhs.value == rhs.value
}
var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)
here they are
You can read below the reasons after each result
Result #1
array.indexOf(object) // nil
In this case the isEqual method is used, so you are just comparing memory address. And since the object var references an object NOT inside the array, you get nil as result
Result #2
array.indexOf(array.last!) // 2
Again isEqual is used. However here you are passing to indexOf the reference to the last object in the array so it works.
Result #3
array.indexOf { $0 == object } // 2
Here you are explicitly using the == operator you defined so the value property is used.
Result #4
array.filter { $0 == object } // [{NSObject, value 5}]
Again the == operator is used and then the value property is used to check for equality.

Resources