Don't know how to access array data inside dictionary [: []], Swift, iOS - ios

I am stuck with this guy [ : []]. As you can see, currently inside function I am using [0] to access value, but with this type of solution I can only access first element of array. How can I access all values? I tried with for in loop, but couldn't solve it...
Thanks in advance!
func tripServices(tripId: String) {
networkManager.getBookedTripsDetails(endpoint: "/v1/bookings/\(tripId)", tripId: tripId) { [unowned self] (safeTripsDetails) in
DispatchQueue.main.async {
self.bookedTripDetails = self.createScreenData(from: safeTripsDetails!, singleTrip: self.singleTrip!)
self.tableView.reloadData()
}
}
}
func createScreenData(from data: [String: [BookingService]], singleTrip: BookedTripsForView) -> [DetailsAboutServiceForView] {
return data.map{ ( data) -> DetailsAboutServiceForView in
return DetailsAboutServiceForView(id: singleTrip.id,
destination: data.value[0].destination,
tripStatus: data.value[0].status,
tripStartTime: data.value[0].startDate,
serviceName: data.value[0].serviceName,
serviceId: data.value[0].serviceId)
}
}

If you have a dictionary of arrays, and you want your output to be a single array containing all of the arrays combined into one array of a different type, there are various ways you could do that.
Rather than trying to work out your data types, I banged out an example using simple structs:
//Source struct
struct Person {
let name: String
let age: Int
}
//Output struct
struct Employee {
let name: String
let age: Int
let salary: Int?
}
let dictOfArrays = ["key1": [Person(name: "Bob", age: 36),
Person(name: "Felecia", age: 27),
Person(name: "Armin", age: 19)],
"key2": [Person(name: "Janet", age: 57),
Person(name: "John", age: 12),
Person(name: "Karen", age: 43)]
]
//Create an empty array for the output
var output = [Employee]()
//Loop through the dictionaries
dictOfArrays.forEach { (_, values) in
values.forEach { person in
//Only those who are >=18 can be employees
if person.age >= 18 {
output.append( Employee(name: person.name, age: person.age, salary: nil))
}
}
}
//Logs the resulting array of Employee structs.
output.forEach { print($0) }
Edit:
As pointed out by Alexander in his comment, you can do the above in one statement without creating an array var and appending to it using a combination of flatmap, filter, and map:
let output = dictOfArrays.flatMap { (_, values) in
values.filter { $0.age >= 18 }
.map { person in
Employee(name: person.name, age: person.age, salary: nil)
}
}

Related

How to filter an array of objects with unique properties?

Forgive the contrived example below, but how can I filter like this? Using a Set to dedup isn't an option since my real data objects have another property that is unique for each.
struct MyDataObject {
var startDate: Date
var endDate: Date
}
let dataObject1 = MyDataObject(startDate: Date().startOfDay(), endDate: Date().startOfDay())
let duplicateDataObject = MyDataObject(startDate: Date().startOfDay(), endDate: Date().startOfDay())
let array = [dataObject1, duplicateDataObject]
//How to filter to end up with an array of data objects with a unique start date?
https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md
import Algorithms
array.uniqued(on: \.startDate)
Or, if you need more control of which elements get chosen:
array.uniqued(on: \.startDate) { [$0, $1].max(by: \.endDate)! }
import struct OrderedCollections.OrderedDictionary
public extension Sequence {
#inlinable func uniqued<Subject: Hashable>(
on projection: (Element) throws -> Subject,
uniquingWith combine: (Element, Element) throws -> Element
) rethrows -> [Element] {
try OrderedDictionary(keyed(by: projection), uniquingKeysWith: combine)
.values
.elements
}
#inlinable func max<Comparable: Swift.Comparable>(
by getComparable: (Element) throws -> Comparable
) rethrows -> Element? {
try self.max {
try getComparable($0) < getComparable($1)
}
}
}
public extension Sequence {
#inlinable func keyed<Key: Hashable>(
by key: (Element) throws -> Key
) rethrows -> [KeyValuePairs<Key, Element>.Element] {
try map { (try key($0), $0) }
}
}
Do you mean filter so you only keep elements that appear exactly once in the original collection?
import OrderedCollections
struct A {
let id: String
let thing: String
}
let things: [A] = [
.init(id: "1", thing: "thing"),
.init(id: "2", thing: "thing"),
.init(id: "3", thing: "thing"),
.init(id: "2", thing: "thing"),
.init(id: "1", thing: "thing"),
.init(id: "4", thing: "thing"),
.init(id: "2", thing: "thing"),
]
let uniqueThings = OrderedDictionary<String, [A]>(grouping: things, by: \.id)
.filter { $0.value.count == 1 }
.values
Gives just the A's with unique id's (of 3 and 4 in this example)
Or do you mean just to deduplicate, like https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md uniqued(on:) and if so which element should be retained if there are more than one, the first seen, the last etc?

How to extract one type of value from an array to create a separate array one by one?

I am trying to extract just the id's from the Properties Array to form a separate array. So far I have come up with this:
struct Interactions: View {
#State var A: [Properties] = [
.init(id: 5, name: "Five"),
.init(id: 8, name: "Eight"),
.init(id: 2, name: "Two")
]
var body: some View {
List(loadIdArray(), id: \.self) { i in
Text("\(i)")
}
}
func loadIdArray() -> [Int] {
let ids: [Int] = [1, 2]
for i in 0 ..< self.A.count {
let ids = [self.A[i].id, self.A[i + 1].id]
return ids
}
return ids
}
}
The problem is that I would have to manually type each [self.A[I + ...].id] which defeats the purpose of the function.
The answer I want is for loadIdArray() = [5, 8, 2] and for it to do this automatically depending on how many items are in the 'A' array.
how about using this:
func loadIdArray() -> [Int] {
return self.A.map { $0.id }
}

How to filter object-array with duplicated items name based on conditions in Swift

I have an object array with duplicated object properties value for some objects and I need to filter it according to some object properties conditions
I have something similar to the following code:
Model:
struct MyObject {
var name: String
var id: String
var times: Int
var value: Double
}
Array:
Let objectArray = [
MyObject(name: “Obj1”, id: “123”, times: 1, value: 3.0),
MyObject(name: “Obj2”, id: “456”, times: 1, value: 2.3),
MyObject(name: “Obj3”, id: “789”, times: 1, value: 1.0),
MyObject(name: “Obj2”, id: “456”, times: 2, value: 3.3),
MyObject(name: “Obj2”, id: “456”, times: 3, value: 4.7),
MyObject(name: “Obj4”, id: “212”, times: 1, value: 2.4)
]
I have implemented an extension Array function to remove the duplicated items in array but it's not enough on this case:
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element] {
var uniqueKeys = Set<String>()
return filter{ uniqueKeys.insert("\(keyValue($0))").inserted }
}
I need to filter objectArray to obtain an array without duplicated objects with the condition to maintain the highest value for times property, that means, the final result should be:
Expected result:
filteredObjectArray = [
MyObject(name: “Obj1”, id: “123”, times: 1, value: 3.0),
MyObject(name: “Obj3”, id: “789”, times: 1, value: 1.0),
MyObject(name: “Obj2”, id: “456”, times: 3, value: 4.7),
MyObject(name: “Obj4”, id: “212”, times: 1, value: 2.4)
]
With Obj2 repeated item filtered by times property = 3
Here is a solution using reduce(into:) to filter out the unique objects
let uniqueAndHighest = objectArray.reduce(into: [MyObject]()) { array, object in
if let index = array.firstIndex(where: { $0.id == object.id }) {
if array[index].times < object.times {
array[index] = object
}
} else {
array.append(object)
}
}
and an alternative solution using Dictionary(grouping:)
let uniqueAndHighest = Dictionary(grouping: objectArray) {$0.id}
.values
.compactMap { $0.max {$0.times < $1.times}}
You can use reduce in this case:
let resultArray = objectArray.reduce([]) { (nextResult, obj) -> [MyObject] in
var tempArray = nextResult
if let existObj = tempArray.first(where: { $0.name == obj.name && $0.id == obj.id }) {
if obj.times > existObj.times {
tempArray.removeAll(where: { $0.name == existObj.name })
tempArray.append(obj)
}
} else {
tempArray.append(obj)
}
return tempArray
}

Protractor - How to check if object is contained in another object?

I have two objects:
let obj1 = {
FirstName: "John",
LastName: "Doe",
age: 20,
color: "Purple"
}
let obj2 = {
FirstName: "John",
LastName: "Doe",
color: "Purple
}
As you can see, I got two almost similar 2 objects.
The only difference is that the second object doesn't have "age" property in it.
I want to compare between them and check if the second object is "contained" in the first one.
I tried
expect(obj1).toContain(obj2);
It doesn't work.
Is that even possible option?
If the objects are the same you can use Equatable, for example:
struct Person: Equatable {
var name: String?
var color: UIColor?
var LastName: String?
}
var person1 = Person()
person1.name = "roei"
person1.LastName = "b"
person1.color = .blue
var person2 = Person()
person2.name = "roei"
person2.LastName = "b"
person2.color = .blue
if person1 == person2 {
print("good")
}
it will print good

Sorting Struct array in Swift 4

I've got the below struct and would like to sort the items within sessions by startTime field. I'm completely lost on how to do this.
I tried:
let sortedArray = sessionsData?.items.sorted{ ($0["startTime"] as! String) < ($1["startTime"] as! String) }
but that just gives me an error about no subscript members?
Any pointers would really be appreciated, thank you.
public struct sessions: Decodable {
let status: String?
let start: Int?
let count: Int?
let items: [sessionInfo]?
let itemsCount: Int?
let multipart: Bool?
let startTime: Int?
let endTime: Int?
}
public struct sessionInfo: Decodable {
let name: String?
let datalist: String?
let sessionType: Int?
let status: Int?
let backupType: Int?
let startTime: Int?
let endTime: Int?
let owner: String?
let numOfErrors: Int?
let numOfWarnings: Int?
let flags: Int?
}
I tried the below, but get an error:
var sortedArray = sessionsData?.items?.sorted(by: { (lhs, rhs) -> Bool in
return lhs.startTime < rhs.startTime
})
error:
Binary operator '<' cannot be applied to two 'Int?' operands
Try below code, it sorts the struct in asc order but it pushes nil timestamps to bottom.
if you want nil timestamps to be at top, make all nil checks in below code to return opposite of what i return in below code.
var sortedArray = sessionsData?.items?.sorted(by: { (lhs, rhs) -> Bool in
if let lhsTime = lhs.startTime, let rhsTime = rhs.startTime {
return lhs.startTime < rhs.startTime
}
if lhs.startTime == nil && rhs.startTime == nil {
// return true to stay at top
return false
}
if lhs.startTime == nil {
// return true to stay at top
return false
}
if rhs.startTime == nil {
// return false to stay at top
return true
}
})
You should access the fields directly and not through subscripts.
let sortedArray = sessionsData?.items.sorted(by: {$0.startTime < $1.startTime})
You could write this (tested in playground) :
var sortedArray = sessionsData?.items?.sorted(by: { (lhs, rhs) -> Bool in return (lhs.startTime ?? 0) < (rhs.startTime ?? 0) })
If one is optional, you do not crash, even though result of comparison is meaningless
You have default High Order Function for sorting the struct array in ascending and descending order
Example
let roster: [TeamMember] = [.init(id: 1, name: "Abishek", age: 19),
.init(id: 2, name: "Dinesh", age: 22),
.init(id: 3, name: "Praveen", age: 24),
.init(id: 4, name: "Sam", age: 25),
.init(id: 5, name: "David", age: 21)]
let descendingSorted = roster.sorted{$0.name > $1.name} // for descending order
let ascendingSorted = roster.sorted{$0.name < $1.name} // for ascending order
print(descendingSorted)
print(ascendingSorted)
Your Output
// for descending order
[TeamMember(id: 4, name: "Sam", age: 25.0),
TeamMember(id: 3, name: "Praveen", age: 24.0),
TeamMember(id: 2, name: "Dinesh", age: 22.0),
TeamMember(id: 5, name: "David", age: 21.0),
TeamMember(id: 1, name: "Abishek", age: 19.0)]
// for ascending order
[TeamMember(id: 1, name: "Abishek", age: 19.0),
TeamMember(id: 5, name: "David", age: 21.0),
TeamMember(id: 2, name: "Dinesh", age: 22.0),
TeamMember(id: 3, name: "Praveen", age: 24.0),
TeamMember(id: 4, name: "Sam", age: 25.0)]
And we have another one method for sorting is SortComparator. we sort the result based on ComparisonResult
let descendingSorted1 = roster.sorted { teamMember1, teamMember2 in
return teamMember1.name.compare(teamMember2.name) == .orderedDescending
} // for descending order
let ascendingSorted1 = roster.sorted{ teamMember1, teamMember2 in
return teamMember1.name.compare(teamMember2.name) == .orderedAscending
} // for ascending order
print(descendingSorted1)
print(ascendingSorted1)

Resources