Remove duplicate values in a tuple array - swift

I'm working on a project in Swift 3 where I have a tuple array with duplicate values is there a way to save it to a NSSet or to avoid replicating the same value. The structure of my tuple array as follow.
var selectedSongList : [(title: String, healerName: String, trackUrl: String, trackID: String, imageUrl: String)] = []
Thus later I'm using this to poplate my UITableView

There are two ways to do it.
Solution 1
You can create a structure and it should confirm to Hashable and equatable some thing like this
struct Post: Hashable, Equatable {
let id: String
var hashValue: Int { get { return id.hashValue } }
}
func ==(left:Post, right:Post) -> Bool {
return left.id == right.id
}
and to remove your object you can do like this
let uniquePosts = Array(Set(posts))
Solution 2
Create a set out of your array and then make it back to array.

Edit func static func == in Song struct if you have different definition about the same song
struct Song: Equatable {
var title: String?
var healerName: String?
var trackUrl:String?
var trackID:String?
var imageUrl: String?
init(_ title: String, _ healerName:String, _ trackUrl:String, _ trackID:String,_ imageUrl: String) {
self.title = title
self.healerName = healerName
self.trackUrl = trackUrl
self.trackID = trackID
self.imageUrl = imageUrl
}
static func == (lhs: Song, rhs: Song) -> Bool {
return lhs.title == rhs.title &&
lhs.healerName == rhs.healerName &&
lhs.trackUrl == rhs.trackUrl &&
lhs.trackID == rhs.trackID &&
lhs.imageUrl == rhs.imageUrl
}
}
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}

Related

Extension for Array where Element is Optional

I have an array of String? like so
var myArray: [String?] = ["2", "banana", nil, "31"];
I have another array of String like so:
var myStringArray: [String] = ["2", "3"]
I wrote an extension for Array to return me an Optional if index is not valid (which avoids the whole app crashing), but I really only want it to return Optional if the Element is NOT optional, and just return Element if it is Optional
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
If i run this against myArray the resulting type is String??
let item = myArray.safeIndex(1) // item is String??
let strItem = myStringArray.safeIndex(99) // strItem is String?
How do i change my extension such that it gives me String? for BOTH cases
Note: If i am explicit about my types, I can get what I want like so
extension Array where Element == String? {
func safeIndex(_ index: Int) -> String? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
However, this approach requires me to write a bunch of extensions for a bunch of types i might have in an array which is a massive meh.
This is kind of tricky but you can create an AnyOptional protocol that requires an associatedtype (Wrapped) and a computed property to return the optional type. Then you can return the element unwrapped if the index is valid otherwise return nil.
protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}
extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}
extension Collection {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
extension Collection {
subscript(safe index: Index) -> Element.Wrapped? where Element: AnyOptional {
indices.contains(index) ? self[index].optional ?? nil : nil
}
}
var myArray: [String?] = ["2", "banana", nil, "31"]
var myStringArray: [String] = ["2", "3"]
let item = myArray[safe: 1] // item is String?
let strItem = myStringArray[safe: 99] // strItem is String?
I think what you want could be achieved with two extensions and a dummy protocol, which would constrain the array's Element. Something like that:
protocol OptionalType {}
extension Optional: OptionalType {}
extension Array where Element: OptionalType {
func safeIndex(_ index: Int) -> Element {
return self[index]
}
}
extension Array {
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
Then this would work:
var myArray: [String?] = ["2", "banana", nil, "31"];
var myStringArray: [String] = ["2", "3"]
let item = myArray.safeIndex(1)
print(type(of: item)) // Optional<String>
let strItem = myStringArray.safeIndex(99)
print(type(of: strItem)) // Optional<String>

How to implement a 'next' property to a CaseIterable enum in Swift

I'm trying to add a next var to an enum. I am able to do so for a specific enum but would like to extend it generically so that I can obtain the 'next' enum case from an enum value just by specifying an enum with a protocol, ex CaseNextIterable
enum MyEnum: CaseIterable { // 'next' here is possible thanks to 'CaseIterable' protocol
case a, b, c
// returns the next case, or first if at end of sequence
// ie. a.next == b, c.next == a
var next: Self {
var r: Self!
for c in Self.allCases + Self.allCases { // not efficient
if r != nil {
r = c
break
}
if c == self {
r = self
}
}
return r
}
}
You can extend CaseIterable constraining Self to Equatable. Then you just need to find the index after the firstIndex of your CaseItareble enumeration and return the element at that position. If the index is equal to the endIndex of all cases just return the first element.
extension CaseIterable where Self: Equatable {
private var allCases: AllCases { Self.allCases }
var next: Self {
let index = allCases.index(after: allCases.firstIndex(of: self)!)
guard index != allCases.endIndex else { return allCases.first! }
return allCases[index]
}
}
Another option is to constrain AllCases to BidirectionalCollection. This will allow you to get the last element of you enumeration, check if it is equal to self and return the first element without the need to iterate your whole collection:
extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
var allCases: AllCases { Self.allCases }
var next: Self {
guard allCases.last != self else { return allCases.first! }
return allCases[allCases.index(after: allCases.firstIndex(of: self)!)]
}
}
expanding on CaseIterable next and previous properties:
extension CaseIterable {
typealias Index = AllCases.Index
var first: Self { allCases.first! }
private var allCases: AllCases { Self.allCases }
private static func index(after i: Index) -> Index { allCases.index(after: i) }
}
extension CaseIterable where AllCases: BidirectionalCollection {
var last: Self { allCases.last! }
private static func index(before i: Index) -> Index { allCases.index(before: i) }
}
extension CaseIterable where Self: Equatable {
var index: Index { Self.firstIndex(of: self) }
private static func firstIndex(of element: Self) -> Index { allCases.firstIndex(of: element)! }
}
extension CaseIterable where Self: Equatable, AllCases: BidirectionalCollection {
var previous: Self { first == self ? last : allCases[Self.index(before: index)] }
var next: Self { last == self ? first : allCases[Self.index(after: index)] }
}
Playground testing;
enum Enum: CaseIterable {
case a,b,c
}
let value: Enum = .c
let next = value.next // a
let next2 = next.next // b
let next3 = next2.next // c
let previous = value.previous // b
let previous2 = previous.previous // a
let previous3 = previous2.previous // c
I'm adding this in complement of Leo Dabus' answer, in case people would need the previous extension too.
edit:
I added both next and previous.
The main difference is in the behavior expected.
For out of band:
this solution returns nul
Leo Dabus' solution works like a chained list
extension CaseIterable where Self: Equatable {
var allCases: AllCases { Self.allCases }
/// Using `next` on an empty enum of on the last element returns `nil`
var next: Self? {
guard let currentIndex = allCases.firstIndex(of: self) else { return nil }
let index = allCases.index(after: currentIndex)
// ensure we don't go past the last element
guard index != allCases.endIndex else { return nil }
return allCases[index]
}
var previous: Self? {
guard let currentIndex = allCases.firstIndex(of: self) else { return nil }
// ensure we don't go before the first element
guard currentIndex != allCases.startIndex else { return nil }
let index = allCases.index(currentIndex, offsetBy: -1)
return allCases[index]
}
}

Codable Struct: Get Value by Key

I have for example codable struct User:
struct User: Codable {
// ...
var psnTag: String?
var xblTag: String?
var steamTag: String?
var nintendoTag: String?
var instagramId: String?
// ...
}
and stringKey as String = "psnTag"
How can I get the value from instance by stringKey?
Like this:
let stringKey = "psnTag"
user.hasKeyForPath(stringKey) //return Bool
user.valueForPath(stringKey) //return AnyObject
Start with extending Encodable protocol and declare methods for hasKey and for value
Using Mirror
extension Encodable {
func hasKey(for path: String) -> Bool {
return Mirror(reflecting: self).children.contains { $0.label == path }
}
func value(for path: String) -> Any? {
return Mirror(reflecting: self).children.first { $0.label == path }?.value
}
}
Using JSON Serialization
extension Encodable {
func hasKey(for path: String) -> Bool {
return dictionary?[path] != nil
}
func value(for path: String) -> Any? {
return dictionary?[path]
}
var dictionary: [String: Any]? {
return (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any]
}
}
Now you can use it like this:
.hasKey(for: "key") //returns Bool
.value(for: "key") //returns Any?

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

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