Unwrapping dictionary values Swift - swift

I'm creating an adjacency list in Swift, storing an array of nodes. However, when adding an edge from an existing node I need to check if the from key exists in any of the children, and if it does check if the to value exists in the same. It seems to be a mess s.t.
func addEdge(from: String, to: String) {
//the full code for addEdge is incomplete here
if (children.contains{ $0.nodes[from] != nil}) {
for child in children {
if (child.nodes[from] != nil) {
if (!(child.nodes[from]?.contains{$0 == to})!){
child.nodes[from]?.append(to)
}
}
}
}
}
Children is
var children = [Node]()
and Node is
class Node: Hashable {
var nodes = [String:[String]]()
var hashValue: Int{ return nodes.hashValue }
static func == (lhs: Node, rhs: Node) -> Bool {
return lhs.nodes.keys == rhs.nodes.keys
}
}
Now it works, but seems really ugly. There must be a better way in Swift, but what is it?

Assuming that you do not wish to change the way you have implemented the above code but want to improve readability, you can utilise if let and optional chaining to make your code cleaner and more readable.
func addEdge(from: String, to: String) {
//the full code for addEdge is incomplete here
if children.contains{ $0.nodes[from] != nil } {
for child in children {
if let fromNode = child.nodes[from], fromNode.contains{$0 == to} {
fromNode.append(to)
}
}
}
}
Swift Optional Chaining

Try something like:
if (children.contains{ $0.nodes[from] != nil}) {
children.filter { $0.nodes[from] != nil }.
compactMap { $0.nodes[from] }.
filter { !($0.nodes[from]!.contains{$0 == to}) }.
forEach { $0.nodes[from]?.append(to) }
}

Related

Long form of this Swift syntax

The following will compare the first element's id with compr's id, and return true if matched.
I am confused with {} of .first { } != nil syntax. How does the longer form of this pattern condense to to the following:
private(set) var arr : [Arrs] = []
func isPresent(for compr: Compr) -> Bool {
Arrs.first { comp.id == $0.id } != nil
}
First, change the line to the following so it compiles:
arr.first { compr.id == $0.id } != nil
This is just using trailing closure syntax for the first(where:) method. It can also be written as:
arr.first(where: { compr.id == $0.id }) != nil
But a better way would be to do the following:
arr.contains { compr.id == $0.id }

Swift: Find date in array that is closest in time

This is my first time developing in swift (macOS command line application).
I am iterating over all jpeg files in a folder, take those that don't have GPS info in the EXIF data and find for each one another image with GPS data that is closest in time (Exif Timestamp). Then I want to copy the GPS data from the image.
I have a struct with a filename (String), date (Date) and GPS (Dictionary) and created an array for all the files (I didn't paste the function getFiles() here).
struct stFile {
var name : String
var date : Date
var gps : [String : AnyObject]?
}
Then I sort the array by the date field:
var files = getFiles(folder: "files://FolderToJpegs/")
var filesSorted = files.sorted(by: { $0.date.compare($1.date) == .orderedDescending })
All I could come up with until now is a function that finds files where the date is exactly the same (Skip files where the name is equal because that's the same file we are iterating over currently):
for file in filesSorted {
if (file.gps == nil) {
if let closesdate = filesSorted.first(where: { $0.date.compare(file.date) == ComparisonResult.orderedAscending && $0.name != file.name }) {
print("The closest date to \(file.date) is \(closesdate.date).")
}
}
}
As I'm new to swift I was wondering if there is already an easy way in version 4 to accomplish this. One way I guess would be to nest another iteration within the first one and calculate time differences.
Thanks!
Edit
Okay, so far I came up with a solution that calculates the time differences between one element and each of the others which have gps data (.gps != nil):
for (index, _) in filesSorted.enumerated()
{
if filesSorted[index].gps == nil
{
var timeDiffPrev = Double.greatestFiniteMagnitude
var closestFile:stFile?
for fileWithGPS in filesSorted
{
if(filesSorted[index].name != fileWithGPS.name && fileWithGPS.gps != nil)
{
let timeDiff = abs(filesSorted[index].date.timeIntervalSince(fileWithGPS.date))
if(timeDiff < timeDiffPrev)
{
closestFile = fileWithGPS
}
timeDiffPrev = timeDiff
}
}
if(closestFile != nil)
{
filesSorted[index].gps = closestFile?.gps
}
}
}
If by "closest" you mean "latest" then here is my solution for you:
First some extensions that will help us:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return compactMap { set.insert($0).inserted ? $0 : nil }
}
}
Thanks to this extension we will remove duplicates from collection.
But now we need your struct to conform to Hashable protocol.
extension stFile: Hashable {
static func == (lhs: stFile, rhs: stFile) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
Thanks to above we can create function like this:
func getLatest() -> stFile? {
return files.orderedSet.filter({$0.gps != nil}).sorted(by: {$0.date.compare($1.date) == .orderedDescending}).first
}
where:
orderedSet remove duplicates (files with the same name)
filter remove files without gps data
sorted is sorting whats left from previous operations (and because orderedSet and filter was used
before, it probably has less files to sort)
EDIT:
At first I have misunderstood the question. I've hope that it is what you meant:
func copyGPS() -> [stFile] {
let uniqueFiles = files.orderedSet.sorted(by: {$0.date.compare($1.date) == .orderedDescending})
var filesWithGPS : [stFile] = []
for file in uniqueFiles {
if file.gps == nil {
let gps = uniqueFiles.filter({$0.gps != nil && $0.date > file.date}).first?.gps
filesWithGPS.append(stFile(name: file.name, date: file.date, gps: gps))
} else {
filesWithGPS.append(file)
}
}
return filesWithGPS
}

Enum pattern matching as a parameter to a function call

I've setup a playground with an example:
enum CarType : Equatable {
case wheeled(wheels: Int)
case flying
public static func ==(lhs: CarType, rhs: CarType) -> Bool {
return lhs.enumName == rhs.enumName
}
var enumName: String {
let stuff = "\(self)".split(separator: "(").first!
return String(describing: stuff)
}
}
var typesPresentAtMyParty = [CarType.wheeled(wheels:4), .wheeled(wheels:4), .flying]
let aKnownType = CarType.flying
if case aKnownType = typesPresentAtMyParty[2] {
print("Was the type")
}
func isPresent(type: CarType, inArray: [CarType]) -> Bool {
return inArray.filter {
if case type = $0 {
return true
}
return false
}.first != nil
}
func isWheeled(inArray: [CarType]) -> Bool {
return inArray.filter {
if case .wheeled = $0 {
return true
}
return false
}.first != nil
}
isPresent(type: .flying, inArray: typesPresentAtMyParty)
isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
The last line here does not compile. While i can do if case .wheeled = $0 ignoring associated type as a check, i cannot find a way of doing the same in a function call isPresent(type: CarType, inArray: [CarType]), when sending isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
Is there a way of writing a function that takes only the valid pattern matching part of the enum as a parameter?
It is not possible to pass partially constructed enums to a function. Partially constructed enums are not valid values, and they only work in pattern matching because the compiler has a concrete value to work with - the one from the right side of the pattern.
These being said, you could easily rewrite your functions to better, more swiftier versions.
Firstly, you don't need isPresent, you can simply use contains:
typesPresentAtMyParty.contains { $0 == .flying }
typesPresentAtMyParty.contains { if case . wheeled = $0 { return true } else { return false } }
Similarly, isWheeled can be shortened (and renamed, for better semantics):
func isWheeled(_ carType: CarType) -> Bool {
if case . wheeled = carType { return true } else { return false }
}
which can pe passed to contains:
let hasWeeled = typesPresentAtMyParty.contains(where: isWheeled)

RxSwift unwrap optional handy function?

Currently I have created a function unwrapOptional to safely unwrap the optional input in the stream.
func unwrapOptional<T>(x: Optional<T>) -> Observable<T> {
return x.map(Observable.just) ?? Observable.empty()
}
let aOpt: String? = "aOpt"
_ = Observable.of(aOpt).flatMap(unwrapOptional).subscribeNext { x in print(x)}
let aNil: String? = nil
_ = Observable.of(aNil).flatMap(unwrapOptional).subscribeNext { x in print(x)}
let a: String = "a"
_ = Observable.of(a).flatMap(unwrapOptional).subscribeNext { x in print(x)}
// output
aOpt
a
What I want to archive is to create a handy function instead of using flatMap(unwrapOptional), for example
Observable.of(a).unwrapOptional()
Something I tried to do, but it never compiles...
extension ObservableType {
func unwrapOptional<O : ObservableConvertibleType>() -> RxSwift.Observable<O.E> {
return self.flatMap(unwrapOptional)
}
}
You want the unwrapOptional method to only work on observables that have optional type.
So you somehow have to constraint the Element of Observable to conform to the Optional protocol.
extension Observable where Element: OptionalType {
/// Returns an Observable where the nil values from the original Observable are
/// skipped
func unwrappedOptional() -> Observable<Element.Wrapped> {
return self.filter { $0.asOptional != nil }.map { $0.asOptional! }
}
}
Unfortunately, Swift does not define such a protocol (OptionalType). So you also need to define it yourself
/// Represent an optional value
///
/// This is needed to restrict our Observable extension to Observable that generate
/// .Next events with Optional payload
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
/// Implementation of the OptionalType protocol by the Optional type
extension Optional: OptionalType {
var asOptional: Wrapped? { return self }
}
checkout unwrap at https://github.com/RxSwiftCommunity/RxSwift-Ext :)
or https://github.com/RxSwiftCommunity/RxOptional
For now, you should use RxOptional for your personal needs
However, RxSwift-Ext will be growth exponentially in next 2-3 months :)
RxSwift now supports compactMap(). So, now you can do things like:
func unwrap(_ a: Observable<Int?>) -> Observable<Int> {
return a.compactMap { $0 }
}
Here's a version without needing OptionalType (from https://stackoverflow.com/a/36788483/13000)
extension Observable {
/// Returns an `Observable` where the nil values from the original `Observable` are skipped
func unwrap<T>() -> Observable<T> where Element == T? {
self
.filter { $0 != nil }
.map { $0! }
}
}

Comparing objects in an Array extension causing error in Swift

I'm trying to build an extension that adds some of the convenience functionality of NSArray/NSMutableArray to the Swift Array class, and I'm trying to add this function:
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
But unfortunately, this line:
if object == objectToCompare {
Is giving the error:
could not find an overload for '==' that accepts the supplied arguments
Question
What am I doing wrong to cause this error?
Example
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare {
return idx
}
}
}
return nil
}
}
Actually there is no need to implement indexOfObject:; there is a global function find(array, element) already.
You can always create an extension that uses NSArray's indexOfObject, e.g:
extension Array {
func indexOfObject(object:AnyObject) -> Int? {
return (self as NSArray).indexOfObject(object)
}
}
You can specify that your array items can be compared with the <T : Equatable> constraint, then you can cast your object into T and compare them, e.g:
extension Array {
func indexOfObject<T : Equatable>(o:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
let to = objectToCompare as T
if o == to {
return idx
}
}
}
return nil
}
}
My guess is that you have to do something like this:
func indexOfObject<T: Equatable>(object: T) -> Int? {
and so on.
Here's a relevant example from Apple's "The Swift Programming Language" in the "Generics" section:
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
The key idea here is that both value and valueToFind must of a type that is guaranteed to have the == operator implemented/overloaded. The <T: Equatable> is a generic that allows only objects of a type that are, well, equatable.
In your case, we would need to ensure that the array itself is composed only of objects that are equatable. The Array is declared as a struct with a generic <T> that does not require it to be equatable, however. I don't know whether it is possible to use extensions to change what kind of types an array can be composed of. I've tried some variations on the syntax and haven't found a way.
You can extract the compare part to another helper function, for example
extension Array {
func indexOfObject(object: T, equal: (T, T) -> Bool) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if equal(object, objectToCompare) {
return idx
}
}
}
return nil
}
}
let arr = [1, 2, 3]
arr.indexOfObject(3, ==) // which returns {Some 2}
You were close. Here's a working extension:
extension Array {
func indexOfObject<T: Equatable>(object:T) -> Int? {
if self.count > 0 {
for (idx, objectToCompare) in enumerate(self) {
if object == objectToCompare as T {
return idx
}
}
}
return nil
}
}
Swift had no way of knowing if object or objectToCompare were equatable. By adding generic information to the method, we're then in business.