Swift Collection extension: pick every other item - swift

I have written the following extension for Collection in Swift that returns a new Collection of every other element from a starting index.
extension Collection {
func everyOtherElement(from theIndex: Int = 0) -> [Element] {
if theIndex >= self.count { return self as! [Element] }
let start = self.index(startIndex, offsetBy: theIndex)
let end = self.endIndex
var everyOther = [Element]()
var iter = start
while iter != end {
everyOther.append(self[iter])
let next = index(after: iter)
if next == end { break }
iter = index(after: next)
}
return everyOther
}
}
There are probably ways to improve the code but my issue is when the Collection is a Dictionary. The extension works but returns an Array of Tuples [(key: key, value: value)]
I would like the extension to return a Dictionary.
I have tried Dictionary(uniqueKeysWithValuesMethod) which works fine once applied to the return of the everyOtherElement method but I can't seem to find a way to make it so directly.
var myDictionary = ["EN" : "Cheers", "SV" : "Skåll", "ES" : "Salud" ].everyOtherElement()
// returns [(key: "EN", value: "Cheers"),(key: "ES", value: "Salud")]
Dictionary(uniqueKeysWithValues: myDictionary.map { ($0.key, $0.value)})
// returns ["EN" : "Cheers", "ES" : "Salud" ]
Thanks for your help!

As other have mentioned a Dictionary is an unordered collection. If you would like to return a Dictionary you can simply extend dictionary and filter every other element. Note that there is no guarantee that the dictionary will keep the order that the key value pairs were enterer. That being said you can accomplish what you want as follow:
extension Dictionary {
var everyOtherElements: Dictionary {
var bool = true
return filter { _ in
defer { bool = !bool }
return bool
}
}
}
let dict = ["EN" : "Cheers", "SV" : "Skåll", "ES" : "Salud" ]
// returns [(key: "EN",
let everyOtherElements = dict.everyOtherElements // ["ES": "Salud", "SV": "Skåll"]
Regarding your comment
I understand from your reply that I can't extend all collection with
the same code, I need to break it down per type?
You don't need to extend every single element type, you can for instance extend RangeReplaceableCollection protocol and return Self which will englobe Strings as well:
extension RangeReplaceableCollection {
mutating func removeEvenIndexElements() {
var bool = true
removeAll { _ in
defer { bool = !bool }
return bool
}
}
mutating func removeOddIndexElements() {
var bool = false
removeAll { _ in
defer { bool = !bool }
return bool
}
}
func evenIndexElements() -> Self {
var bool = true
return filter { _ in
defer { bool = !bool }
return bool
}
}
func oddIndexElements() -> Self {
var bool = false
return filter { _ in
defer { bool = !bool }
return bool
}
}
}
var alphabet = "abcdefghijklmnopqrstuvwxyz"
alphabet.removeEvenIndexElements()
alphabet // "bdfhjlnprtvxz"
var arr = [1,2,3,4,5,6,7,8,9,0]
arr.removeOddIndexElements()
arr // [1, 3, 5, 7, 9]

Related

How would one implement a bidirectional map in Swift?

I am currently in need of a performant bidirectional map. In Swift, a dictionary can be reversed, however, that will return a tuple of the types it is made of, not a counterpart dictionary.
Is there a library for that or does someone have ideas on how to address this issue?
Thanks
With Swift 4 you could easily make your own using a generic struct:
struct BidiMap<F:Hashable,T:Hashable>
{
private var _forward : [F:T]? = nil
private var _backward : [T:F]? = nil
var forward:[F:T]
{
mutating get
{
_forward = _forward ?? [F:T](uniqueKeysWithValues:_backward?.map{($1,$0)} ?? [] )
return _forward!
}
set { _forward = newValue; _backward = nil }
}
var backward:[T:F]
{
mutating get
{
_backward = _backward ?? [T:F](uniqueKeysWithValues:_forward?.map{($1,$0)} ?? [] )
return _backward!
}
set { _backward = newValue; _forward = nil }
}
init(_ dict:[F:T] = [:])
{ forward = dict }
init(_ values:[(F,T)])
{ forward = [F:T](uniqueKeysWithValues:values) }
subscript(_ key:T) -> F?
{ mutating get { return backward[key] } set{ backward[key] = newValue } }
subscript(_ key:F) -> T?
{ mutating get { return forward[key] } set{ forward[key] = newValue } }
subscript(to key:T) -> F?
{ mutating get { return backward[key] } set{ backward[key] = newValue } }
subscript(from key:F) -> T?
{ mutating get { return forward[key] } set{ forward[key] = newValue } }
var count:Int { return _forward?.count ?? _backward?.count ?? 0 }
}
var bd = BidiMap( [1:"A", 2:"B", 3:"C"] )
bd[1] // "A"
bd["B"] // 2
bd[4] = "D"
bd[to:"D"] // 4
bd[from:4] // "D"
var int2int = BidiMap( [1:2, 5:3] )
int2int[from:1] // 2
int2int[to:3] // 5
[EDIT] improved performance a bit by delaying rebuilding of mirror dictionary until it is actually referenced.

Generic Extension to Array Not Working

I've been playing around with Generics and Extensions to existing types in Swift 3. I wrote two generic Array functions that extends Array with find-and-replace methods, named replaced() and replace(). The replaced() function works as intended but the replace() function has a compile time error. Here is the code and a test of one of the methods.
extension Array {
func replaced<T: Equatable>(each valueToReplace: T, with newValue: T) -> [T] {
var newArray:[T] = []
for index:Int in 0..<self.count {
if let temp = self[index] as? T, temp == valueToReplace{
newArray.append(newValue)
}else{
newArray.append(self[index] as! T)
}
}
return newArray
}
mutating func replace<T: Equatable>(each valueToReplace: T, with newValue: T) {
for index:Int in 0..<self.count {
if let temp = self[index] as? T, temp == valueToReplace {
// FIXME: self[index] = newValue
}
}
return
}
}
var j = [1,2,3,4,3,6,3,8,9]
var newArray = j.replaced(each: 3, with: 0)
I get a compile time error on the second method, replace(), at the line commented out with "//FIXME:" annotation. The compile time error says, "Ambiguous reference to member 'subscript'".
How can I fix the replace() code so it works?
Give this a shot
extension Array where Element: Equatable {
func replaced (each valueToReplace: Element, with newValue: Element) -> [Element] {
var newArray = [Element]()
newArray.reserveCapacity(self.count)
for element in self {
let newElement = (element == valueToReplace) ? newValue : element
newArray.append(newElement)
}
return newArray
}
mutating func replace(each valueToReplace: Element, with newValue: Element) {
for (i, element) in self.enumerated() {
if element == valueToReplace { self[i] = newValue }
}
}
}
var j = [1,2,3,4,3,6,3,8,9]
var newArray = j.replaced(each: 3, with: 0)
It would be better to remove the redundancy by just making replaced delegate to replace:
extension Array where Element: Equatable {
func replaced(each valueToReplace: Element, with newValue: Element) -> [Element] {
var copy = self
copy.replace(each: valueToReplace, with: newValue)
return copy
}
mutating func replace(each valueToReplace: Element, with newValue: Element) {
for (i, element) in self.enumerated() {
if element == valueToReplace { self[i] = newValue }
}
}
}

Using map with filter

I want to iterate over an array of arrays so search for a specific item and return true if exits.
var fruits = ["apple", "banana"]
var names = ["ivan", "john", "maria"]
var mainArray = [fruits, names]
// i want to return true if theres a name/fruit that is "john"
func search() -> Bool {
for object in mainArray {
if (object.filter { $0 == "john" }).count > 0 {
return true
}
}
return false
}
search()
This works but there a shorter version using .map and avoiding for object in mainArray ?
like mainArray.map.filter... ?
var fruits = ["apple", "banana"]
var names = ["ivan", "john", "maria"]
var mainArray = [fruits, names]
func search() -> Bool {
return mainArray.contains { $0.contains("john") }
}
Or, in Swift 1:
func search() -> Bool {
return contains(mainArray) {
inner in contains(inner) {
$0 == "john"
}
}
}
As was pointed out by #AirspeedVelocity, you can actually make those closures have shorthand arguments:
func search() -> Bool {
return contains(mainArray) { contains($0) { $0 == "john" } }
}
I know this doesn't use map and filter, but why don't you use contains ?
func search() -> Bool {
for object in mainArray {
if contains(object, "john") {
return true
}
}
return false
}

Insertion-Order Dictionary (like Java's LinkedHashMap) in Swift?

Is there a standard swift class that is a Dictionary, but keeps keys in insertion-order like Java's LinkedHashMap? If not, how would one be implemented?
Didn't know of one and it was an interesting problem to solve (already put it in my standard library of stuff) Mostly it's just a matter of maintaining a dictionary and an array of the keys side-by-side. But standard operations like for (key, value) in od and for key in od.keys will iterate in insertion order rather than a semi random fashion.
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
private var _dictionary:Dictionary<KeyType, ValueType>
private var _keys:Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = map(dictionary.keys) { $0 }
}
subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key)
}
else {
self.updateValue(newValue!, forKey: key)
}
}
}
mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValueForKey(key)
}
mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
var values:GeneratorOf<ValueType> {
get {
var index = 0
return GeneratorOf<ValueType> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return self._dictionary[key]
}
}
}
}
}
extension OrderedDictionary : SequenceType {
func generate() -> GeneratorOf<(KeyType, ValueType)> {
var index = 0
return GeneratorOf<(KeyType, ValueType)> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return (key, self._dictionary[key]!)
}
}
}
}
func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
Swift 5 version:
// OrderedDictionary behaves like a Dictionary except that it maintains
// the insertion order of the keys, so iteration order matches insertion
// order.
struct OrderedDictionary<KeyType: Hashable, ValueType> {
private var _dictionary: Dictionary<KeyType, ValueType>
private var _keys: Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity: Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary: Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = dictionary.keys.map { $0 }
}
subscript(key: KeyType) -> ValueType? {
get {
_dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key: key)
} else {
_ = self.updateValue(value: newValue!, forKey: key)
}
}
}
mutating func updateValue(value: ValueType, forKey key: KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key: KeyType) {
_keys = _keys.filter {
$0 != key
}
_dictionary.removeValue(forKey: key)
}
mutating func removeAll(keepCapacity: Int) {
_keys = []
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: keepCapacity)
}
var count: Int {
get {
_dictionary.count
}
}
// keys isn't lazy evaluated because it's just an array anyway
var keys: [KeyType] {
get {
_keys
}
}
var values: Array<ValueType> {
get {
_keys.map { _dictionary[$0]! }
}
}
static func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
static func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
}
extension OrderedDictionary: Sequence {
public func makeIterator() -> OrderedDictionaryIterator<KeyType, ValueType> {
OrderedDictionaryIterator<KeyType, ValueType>(sequence: _dictionary, keys: _keys, current: 0)
}
}
struct OrderedDictionaryIterator<KeyType: Hashable, ValueType>: IteratorProtocol {
let sequence: Dictionary<KeyType, ValueType>
let keys: Array<KeyType>
var current = 0
mutating func next() -> (KeyType, ValueType)? {
defer { current += 1 }
guard sequence.count > current else {
return nil
}
let key = keys[current]
guard let value = sequence[key] else {
return nil
}
return (key, value)
}
}
I didn't found way to make values 'lazy'.. need more research

Is it possible to override find(sequence, element) function in swift?

I have an array of MyObject where my object is like this:
class MyObject: Equatable {
var name: String?
var age: Int?
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
func ==(first: MyObject, second: MyObject) -> Bool {
return first.name == second.name
}
Let's say my array is:
var array: [MyObject]
Is there a way to use find(sequence, element) function like this? :
var myFoundObject = find(array, "name_of_the_object_I_m_looking_for")
Thanks for your help.
As #Kirsteins mentioned, find returns the index.
You can easily implement your own find which accept testing function, like filter:
func find<C:CollectionType, U where C.Generator.Element == U>(domain:C, isValue:U -> Bool) -> C.Index? {
for idx in indices(domain) {
if isValue(domain[idx]) {
return idx
}
}
return nil
}
// usage
let array = ["foo", "bar", "baz"]
if let found = find(array, { $0.hasSuffix("az") }) {
let obj = array[found]
}
Using this, you can:
if let found = find(array, { $0.name == "name_of_the_object_I_m_looking_for" }) {
let myFoundObject = array[found]
}
find function returns the index of object in collection, not the object.
You have to iterate ovre array to find the object that matches you criteria, for example:
var myObject: MyObject? = {
for object in array {
if "name_of_the_object_I_m_looking_for" == object.name {
return object
}
}
return nil
}()