I have the following function (example), which I would like to write in a functional way:
func passengerForId(_ id: Int) -> Passenger?
{
for car in cars
{
for passenger in car.passengers
{
if passenger.id == id
{
return passenger
}
}
}
return nil
}
What would be the best way?
I've tried the following:
func passengerForId(_ id: Int) -> Passenger?
{
return cars.first(where: { $0.passengers.contains(where: { $0.id == id } ) })
}
However, that obviously returns the car containing the requested passenger, and not the passenger itself.
Another option would something like:
func passengerForId(_ id: Int) -> Passenger?
{
let passengers = cars.flatMap { car in car.passengers }
return passengers.first(where: { $0.id == id })
}
or
func passengerForId(_ id: Int) -> Passenger?
{
return cars.flatMap { car in car.passsengers.filter { $0.id == id }}.first
}
However, that seems less efficient than the original, as it will loop over all the areas.
Is there a better way to do this?
You can create a (lazy) flattened collection of all passengers, and pick
the first matching one:
func passengerForId(_ id: Int) -> Passenger? {
return cars.lazy.flatMap({ $0.passengers }).first(where: { $0.id == id } )
}
Related
I'm really struggling to comprehend how to (if even possible) to convert a generics function written in Typescript into something I can use in Swift.
export type Filter<T> = (value: T) => boolean
export function isKeyEqualToValue<T>(key: keyof T) {
return function (value: T[keyof T]): Filter<T> {
return (object: T) => object[key] === value
}
}
isKeyEqualToValue<T>('key')(someObject.key)
const filters = userFilters.map(userFilterSet => isEvery(buildAlertFilter(userFilterSet)))
const isMatch = isAny(filters)
return flow.reduce((feed: String[], obj: SomeType) => {
if (!isMatch(obj)) return feed
return [
...feed,
{
...obj
},
]
}, [])
}
I would like to be able to input a struct model in for T and check if the inputted value matches the key. Would greatly appreciate some guidance here!
EDIT:
I've added how the method is being called and used. Essentially I'm trying to avoid doing an algorithm O(n)^2 and so I'm trying to build a list of filters based on our user's choice. Then cross check the bulk of my data (SomeType) with those built filters.
I'm working to translate another function using the similar principles.
export function hasInArray<T>(key: keyof T) {
return function (values: Array<any>): Filter<T> {
return (object: T) => values.includes((object[key] as unknown) as string)
}
}
This is what I have so far.
func notInArray<Root, Value>(for keyPath: KeyPath<Root, Value>) -> (Array<Any>) -> Filter<Root, Value> {
{ values in { object in !values.contains(where: object[keyPath: keyPath]) } } }
You haven't given how you expect to use this, so I need to make some assumptions. I'm assuming the TypeScript that calls this looks like this:
interface Person {
name: string;
age: number;
}
const key: keyof Person = "name";
const nameTester = isKeyEqualToValue(key);
const person = {name: "Alice", age: 23};
const result = nameTester("Alice")(person);
The equivalent to TypeScript's keyof in Swift is KeyPath. Keeping this as close to the TypeScript syntax as possible to make it easier to see how it maps, this would look like:
typealias Filter<T> = (_ value: T) -> Bool
func isKeyEqualToValue<T, Value>(key: KeyPath<T, Value>) -> (Value) -> (T) -> Bool
where Value: Equatable
{
return { (value: Value) -> Filter<T> in
return { (object: T) in object[keyPath: key] == value }
}
}
struct Person: Equatable {
var name: String
var age: Int
}
let key = \Person.name
let nameTester = isKeyEqualToValue(key: key)
let person = Person(name: "Alice", age: 23)
let result = nameTester("Alice")(person)
To make it better Swift (rather than matching the TypeScript so closely), it would look like:
typealias Filter<Root, Value: Equatable> = (Value) -> (Root) -> Bool
func isEqualToValue<Root, Value>(for keyPath: KeyPath<Root, Value>) -> Filter<Root, Value>
{
{ value in { object in object[keyPath: keyPath] == value } }
}
let nameTester = isEqualToValue(for: key)
Your second example is like the first.
func hasInArray<Root, Values>(for keyPath: KeyPath<Root, Values>) -> (Values.Element) -> (Root) -> Bool
where Values: Sequence, Values.Element: Equatable
{
{ value in { object in object[keyPath: keyPath].contains(value) } }
}
You will almost never want Array<Any>. You need an array of the specific element. But in this case you don't need an array at all; you just need any Sequence.
All this said, I wouldn't do it this way. I think it's much easier to understand if you create a Filter type to manage it.
// A Filter object over a specific Target object (for example, a Person)
struct Filter<Target> {
let passes: (Target) -> Bool
}
// Filters can be created many ways
extension Filter {
// By properties equal to a value
static func keyPath<Value>(_ keyPath: KeyPath<Target, Value>, equals value: Value) -> Filter
where Value: Equatable
{
Filter { target in
target[keyPath: keyPath] == value
}
}
// By properties containing a value
static func keyPath<Seq>(_ keyPath: KeyPath<Target, Seq>, contains value: Seq.Element) -> Filter
where Seq: Sequence, Seq.Element: Equatable
{
Filter { target in
target[keyPath: keyPath].contains(value)
}
}
// By a property being a member of a sequence
static func keyPath<Seq>(_ keyPath: KeyPath<Target, Seq.Element>, isElementOf seq: Seq) -> Filter
where Seq: Sequence, Seq.Element: Equatable
{
Filter { target in
seq.contains(target[keyPath: keyPath])
}
}
// By combining other filters
static func all(of filters: [Filter]) -> Filter {
Filter { target in
filters.allSatisfy { filter in filter.passes(target) }
}
}
}
struct Person {
var name: String
var age: Int
var children: [String]
}
let filter: Filter<Person> = .all(of: [
.keyPath(\.name, equals: "Alice"),
.keyPath(\.children, contains: "Bob"),
.keyPath(\.age, isElementOf: [23, 43]),
])
let alice = Person(name: "Alice", age: 23, children: ["Bob"])
let shouldInclude = filter.passes(alice) // true
I have an item of the class Product. I'm changing a variable within the Product class but my addToCart method below treats the item as if no changes have been made. I'm comparing the products based on the id and the variationId. What am I doing wrong?
import UIKit
class Product: Equatable {
let id: Int
let name: String
var variationId: Int
var quantity: Int
init(id: Int, name: String, variationId: Int, quantity: Int) {
self.id = id
self.name = name
self.variationId = variationId
self.quantity = quantity
}
static func == (lhs: Product, rhs: Product) -> Bool {
return
lhs.id == rhs.id && lhs.variationId == rhs.variationId
}
}
The user can select a different color for the product and in doing so changes the variationId.
The addItemToCart() method checks if the cartItems array contains this product. If the product exists, the quantity gets increased by 1 otherwise the product is added to the array.
var cartItems = [Product]()
func addItemToCart(product: Product) {
if cartItems.contains(product) {
let quantity = product.quantity
product.quantity = quantity + 1
} else {
cartItems.append(product)
}
}
The method above keeps updating the quantity regardless if the variationId is different or not.
You are not updating the correct object. Your addItemToCart(product:) function should be something like this:
func addItemToCart(product: Product) {
if let cartItemIndex = cartItems.firstIndex(of: product) {
cartItems[cartItemIndex].quantity += product.quantity
} else {
cartItems.append(product)
}
}
You can do this as follows, you can also remove the equatable attribute.
var cartItems = [Product]()
func addItemToCart(product: Product) {
if let cardItemIndex = cardItems.firstIndex(where: { $0.id == product.id && $0.variationId == product.variationId}) {
cartItems[cardItemIndex].quantity += 1
} else {
cardItems.append(product)
}
}
I have the next function on Vapor:
func getPartidosHandler(_ req: Request) throws -> Future<[PartidoWSData]> {
return Partido.query(on: req).filter(\.estado == nil).all().map(to: [PartidoWSData].self) { partidos in
var partidosWS: [PartidoWSData] = []
for partido in partidos {
// Something here
}
return partidosWS
}
}
And the next struct PartidoWSData:
struct PartidoWSData: Content {
let idPartido: String
let fecha: String
let sede1: Future<Sede>
let sede2: Future<Sede>
}
My model Partido has two references to Sede, "sede1" and "sede2".
What I want is that the function gives an array of PartidoWSData struct, where I can see two properties of "Partido", "idPartido" and "fecha", and the two Sede related to the model.
How can I do that?
Thanks!
I'm not sure exactly what type of relation exists between Partido and Sedebecause the model wasn't included here, but assuming it's a Parent/Child relation, you should be able to do something like:
func getPartidosHandler(_ req: Request) throws -> Future<[PartidoWSData]> {
return Partido.query(on: req).filter(\.estado == nil).all().flatMap { partidos -> Future<[PartidoWSData]> in
let partidoIDs = try partidos.map { try $0.requireID() }
return Sede.query(on: req).filter(\.partidoID ~~ partidoIDs).map { sedes -> [PartidoWSData] in
return partidos.map { partido -> PartidoWSData in
return PartidoWSData(
id: partido.id
sedes: sedes.filter { $0.partidoID == partido.id }
)
}
}
}
}
The key is using the ~~ operator to do an x IN (...) predicate, following by using Array.filter to get the appropriate results.
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) }
}
I have a Set instance and want to put it into a Dictionary, and associate it with multiple keys so I can lookup/modify it in the future.
Following Python code is what I want to achieve in Swift.
s = set()
D = {}
D["a"] = s
D["b"] = s
D["a"].add("Hello")
D["a"].add("World")
print(D["b"]) # getting {"Hello", "World"} back
I tried something like following in Swift.
var s = Set<String>()
var D = Dictionary<String, Set<String>>()
D["a"] = s // copy of s is assigned
D["b"] = s // another copy of s is assigned
D["a"]!.insert("Hello")
D["a"]!.insert("World")
print(D["b"]!) // empty :(
Since collections in Swift hold value semantics, by the time I put a set into a dictionary, new instance is created. Is there any workaround? I know I could use NSMutableSet instead of Swift's Set, but I want to know how I can approach this by using collections with value semantics if possible.
Ah! Now we get to the heart of it. You just want a reference type based on stdlib rather than using the one that Foundation gives you. That's straightforward to implement, if slightly tedious. Just wrap a Set in a class. If you don't want full SetAlgebra or Collection conformance, you don't have to implement all of these methods. (And you might want some more init methods to make this more convenient, but hopefully those implementations are fairly obvious from your code needs.)
final class RefSet<Element> where Element: Hashable {
private var storage: Set<Element> = Set()
init() {}
}
extension RefSet: Equatable where Element: Equatable {
static func == (lhs: RefSet<Element>, rhs: RefSet<Element>) -> Bool {
return lhs.storage == rhs.storage
}
}
extension RefSet: SetAlgebra {
var isEmpty: Bool { return storage.isEmpty }
func contains(_ member: Element) -> Bool {
return storage.contains(member)
}
func union(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.union(other.storage))
}
func intersection(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.intersection(other.storage))
}
func symmetricDifference(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.symmetricDifference(other.storage))
}
#discardableResult
func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
return storage.insert(newMember)
}
#discardableResult
func remove(_ member: Element) -> Element? {
return storage.remove(member)
}
#discardableResult
func update(with newMember: Element) -> Element? {
return storage.update(with: newMember)
}
func formUnion(_ other: RefSet<Element>) {
storage.formUnion(other.storage)
}
func formIntersection(_ other: RefSet<Element>) {
storage.formIntersection(other.storage)
}
func formSymmetricDifference(_ other: RefSet<Element>) {
storage.formSymmetricDifference(other.storage)
}
}
extension RefSet: Collection {
typealias Index = Set<Element>.Index
var startIndex: Index { return storage.startIndex }
var endIndex: Index { return storage.endIndex }
subscript(position: Index) -> Element {
return storage[position]
}
func index(after i: Index) -> Index {
return storage.index(after: i)
}
}