Is there a better way to create an index of an array? - arrays

I made up a method to add a variable rankto an array of structs.
The array friutsArrayis created like in the function makeFriuts(). After that, the data gets sorted and according to this, every item gets a rank, respectively index.
In the end I need the FriutsWithRankstruct like it is.
But I´m wondering if there is a better, more effective way to to that. Maybe by even skipping the whole Friuts struct:
struct Friuts {
var name: String
var price: Double
}
struct FriutsWithRank {
var name: String
var price: Double
var rank: Int
}
var friutsArray = [Friuts]()
func makeFriuts() {
friutsArray.append(Friuts(name: "mango", price: 1.2))
friutsArray.append(Friuts(name: "banana", price: 0.79))
friutsArray.append(Friuts(name: "orange", price: 2.2))
}
func makeFriutsWithRank(data: [Friuts]) -> [FriutsWithRank] {
let dataSorted = data.sorted { $1.price < $0.price }
var datatoappend = [FriutsWithRank]()
var i = 0
dataSorted.forEach { fruit in
i += 1
let name = fruit.name
let price = fruit.price
let rank = i
let result = FriutsWithRank(name: name, price: price, rank: rank)
datatoappend.append(result)
}
return datatoappend
}
let friutsArrayWithRank = makeFriutsWithRank(data: friutsArray)
With more effective i mean not necessarily less code. I think the two arrays are now created with two iterations. Is it possible to skip the whole Fruits struct and work just with one struct and one iteration?.

I have applied some modification on your code, please read the inline comments. Not much more optimised, but more readable for sure.
// Typo fixed + it is a single Fruit, not Fruits
struct Fruit {
var name: String
var price: Double
}
struct FruitsWithRank {
// You already have a variable holding name and price, Friut
// Lets just reuse Fruit object
var fruit: Fruits
var rank: Int
}
var fruits = [Fruit]()
func makeFruits() {
fruits.append(Fruit(name: "mango", price: 1.2))
fruits.append(Fruit(name: "banana", price: 0.79))
fruits.append(Fruit(name: "orange", price: 2.2))
}
func makeFruitsWithRank(data: [Fruits]) -> [FruitsWithRank] {
let dataSorted = data.sorted { $1.price < $0.price }
var datatoappend = [FruitsWithRank]()
// Use `enumerated` to get index and the object straight away
for (index, fruit) in dataSorted.enumerated() {
// Just init your `FruitsWithRank` with the fruit and the index
let rankedFruit = FruitsWithRank(fruit: fruit, rank: index)
// Add to your array
datatoappend.append(rankedFruit)
}
return datatoappend
}
let fruitsWithRank = makeFruitsWithRank(data: fruitsArray)
EDIT:
Following the edits of your question, i have applied some changes. If you need FruitsWithRank having name and price, you can just create tuples with name and price, and create an array straight away without any loops or appends. You can omit the makeFruitsWithRank function, and sort, enumerate and create your types straight on the tuple array.
struct FruitsWithRank {
var rank: Int
var name: String
var price: Double
}
let rankedFruits: [FruitsWithRank] = [
(name: "mango", price: 1.2),
(name: "banana", price: 0.79),
(name: "orange", price: 2.2)]
.sorted { $0.price < $1.price }
.enumerated()
.map({ return FruitsWithRank(rank: $0 + 1, name: $1.name, price: $1.price) })

In the end this isn't much more efficient than your code, but it is more compact:
func makeFriutsWithRank(data: [Friuts]) -> [FriutsWithRank] {
let dataMapped = data.sorted { $1.price < $0.price }
.enumerated()
.map { FriutsWithRank(name: $1.name, price: $1.price, rank: $0 + 1) }
return dataMapped
}

Is it really necassary to have Struct? Because you already have sorted, the index can serve as rank.

Related

Check if multi dictionary contains a value Swift 4

I want to check if multi dictionary contains a value for example
Below is all my code:
struct listBuyItem: Codable {
let id: Int
let beer: String
let quantity: Int
let totalPrice: Double
}
var cart: [listBuyItem] = [listBuyItem]()
cart.append(listBuyItem(id: beer.id, beer: beer.beerName, quantity: Int((cell?.stepperBeer.value ?? Double(0))), totalPrice: price))
how can I check if "id = 1" exist?
Try this
for eachCart in cart {
if eachCart.id == 1 {
print("id is equal to 1")
}
}

Swift 4 Array Find or First

I'm trying to perform a filter on an array of structs whereby if the element exists return the element, otherwise return the first element in the array. At the moment I have a func to do this, was wondering if there was a more efficient way. Predicate or Array extension?
For example, if this is my array of structures and method:
struct Person {
let name: String
let age: Int
}
let items = [Person(name: "Fred", age: 12),
Person(name: "Bill", age: 14),
Person(name: "Jane", age: 15),
Person(name: "Mary", age: 12)]
// Find the person based on name, if no match, return first item
func filterOrFirst(with name: String? = "") -> Person?
{
if (items.contains(where: {$0.name == name}))
{
return items.first(where: {$0.name == name})
}
return items.first
}
print(filterOrFirst(with: "Bill")) // prints Bill
print(filterOrFirst()) // prints Fred
You can do it like
func filterOrFirst(with name: String? = "") -> Person? {
if let item = items.first(where: {$0.name == name}) {
return item
}
return items.first
}
So you need not to traverse complete array two times. You can use ternary operator here. But it increases compilation time.
You can get idea from this:
var arrData:[String] = ["Cat","Dog","Horse","Sheep"]
func respondTheValue(strSearch:String) -> String{
let stringToSearch = strSearch
if let index = arrData.index(of: stringToSearch){ //Check if data is present or not
return arrData[index]
}else{ // Data not present then return first
return arrData.first!
}
}
And use it Like:
let responseStr = respondTheValue(strSearch: "Dog")
print(responseStr) //Case True : Output -- Dog
let responseStr1 = respondTheValue(strSearch: "Fish")
print(responseStr1) //Case False : Output -- Cat
Hope this helps.

How to mutate Values inside a Array

I need to mutate the following array:
struct Person {
var name: String
var age = 0
}
func showPersonArray() -> [Person] {
var dataArray = [Person]()
dataArray.append(Person(name: "Sarah_Yayvo", age: 29))
dataArray.append(Person(name: "Shanda_Lear", age: 45))
dataArray.append(Person(name: "Heidi_Clare", age: 45))
return dataArray
}
How could I split the "name"-key into two keys: "givenName" and "familyName".
Some nice person gave me this code before:
let arraySeparated1 = dataArray.map { $0.substring(to: $0.range(of: "_")!.lowerBound) }
let arraySeparated2 = dataArray.map { $0.substring(from: $0.range(of: "_")!.upperBound) }
Is it possible to make the mutation inside the struct?
The function showPersonArray() is only for demo and test.
Maybe there is a way to work with a target struct, like this:
struct Persontarget {
var familyname: String
var givenName: String
var age = 0
}
struct Person: Array -> [Persontarget] {
var name: String
var age = 0
// Here the split/mutating code
return [PersonWithTwoNames]
}
Or with an String extension. Possibly my question sounds pretty newby-like, but i´m trying the whole day...
Thanks everyone!
I would write an initializer on the new Person type, which initializes it from the old person type (which I called LegacyPerson):
import Foundation
struct LegacyPerson {
let name: String
let age: Int
}
func getPersonArray() -> [LegacyPerson] {
return [
LegacyPerson(name: "Sarah_Yayvo", age: 29),
LegacyPerson(name: "Shanda_Lear", age: 45),
LegacyPerson(name: "Heidi_Clare", age: 45)
]
}
struct Person {
let familyName: String
let givenName: String
let age: Int
}
extension Person {
init(fromLegacyPerson person: LegacyPerson) {
let index = person.name.range(of: "_")!
self.init(
familyName: person.name.substring(from: index.upperBound),
givenName: person.name.substring(to: index.lowerBound),
age: person.age
)
}
}
let people: [Person] = getPersonArray().map(Person.init)
people.forEach{ print($0) }
Create a method or computer variable for your Person class that returns what you want.
func firstName() -> String {
return self.substring(to: $0.range(of: "_")!.lowerBound)
}
You should not force cast though
with help of computed property defined in an extension
struct Person {
let name: String
let age: Int
}
let person = Person(name: "Maria_Terezia", age: 300)
extension Person {
var names:[String] {
get {
return name.characters.split(separator: "_").map(String.init)
}
}
}
for name in person.names {
print(name)
}
prints
Maria
Terezia

Append a struct inside a property which is an array inside other struct in Swift

Hi All I need to append a struct inside an array which is a property of an other struct, I'm using Swift 3, however is not appending anything and I have no idea why, any help please?
import Foundation
struct Product {
var objectId: String
var name: String
var price: Double
var qty: Int
var img: String
var desc: String
var note: String
}
struct Order {
var objectId: String
var name: String
var detail: [Product]
mutating func addToDetail(_ product: Product) {
detail.append(product)
}
}
class OrderManager: NSObject {
static let shared = OrderManager()
var order: Order?
var orderCreated: Bool {
return self.order != nil
}
var partnerInOrder: String? {
return self.order?.objectId
}
func createOrderIfNeeded(partnerId: String, name: String) {
if self.order == nil {
self.order = Order(objectId: partnerId, name: name, detail: [Product]())
}
}
func addProduct(objectId: String, name: String, price: Double, qty: Int, img: String = "", desc: String = "", note: String = "") {
guard var order = self.order else {
return
}
let hasProduct = order.detail.contains(where: { $0.objectId == objectId })
if !hasProduct {
order.addToDetail(Product(objectId: objectId, name: name, price: price, qty: qty, img: img, desc: desc, note: note))
}
print("has product \(hasProduct)")
print("the whole order is \(self.order)")
}
func addOption() {}
func getProduct() {}
func getOption() {}
fileprivate func storeOrder() {}
}
Is getting inside the addToDetail func of the struct, but always my array is empty
In the following guard statement (in method addProduct of OrderManager)
guard var order = self.order else { /* ... */ }
you create a copy of self.order (given that it is not nil), due to the value semantics of structures in Swift. The subsequent call to addToDetail on this copy will not append a Product instance to the instance variable order of self, but only to the copy which goes out of scope as addProduct goes out of scope.
You could test this theory by replacing the optional binding clause in the guard statement above with a simple nil check (as #MartinR points out below, we don't really need a guard statement (after the fix: no binding), but can just perform an early return in case self.order is nil)
if self.order == nil { return }
Or, remove the explicit nil check altogether, and use optional chaining to decide whether or not to add a product to the order instance (combining the nil check and hasProduct in a single optional chaining clause):
func addProduct(objectId: String, name: String, price: Double, qty: Int, img: String = "", desc: String = "", note: String = "") {
if !(self.order?.detail.contains(where: { $0.objectId == objectId }) ?? true) {
order?.addToDetail(Product(objectId: objectId, name: name, price: price, qty: qty, img: img, desc: desc, note: note))
}
// ... remove the logging
}
This does a poorer job showing the intent of the code, though.

Merge arrays with condition

I would like to merge two arrays with specific condition and update objects that they are containing.
First my struct that is in arrays:
struct Item {
var id:Int
var name:String
var value:Int
}
Second elements for the two arrays:
let fisrt = Item(id: 1, name: "Oleg", value: 3)
let second = Item(id: 2, name: "Olexander", value:5)
let fisrtInSecond = Item(id: 1, name: "Bogdan", value: 6)
let secondInSecond = Item(id: 2, name: "Max", value: 9)
Arrays:
var fisrtArray = [fisrt, second]
let secondArray = [fisrtInSecond, secondInSecond]
I woudl like to use zip and map functions of the collection to achive result. Result is that fisrtArray elements names are updated by id.
Example: fisrtArray = [Item(id: 1, name: "Bogdan", value:3), Item(id: 2, name: "Max", value:5)]
I know how to do this via simple loops. But i am looking for more advanced usage of the functional programing is Swift.
My experiment:
fisrtArray = zip(fisrtArray, secondArray).map()
The main problem i do not know how to write condition in the map function. Condition should be:
if ($0.id == $1.id) {
$0.name = $1.name
}
From the comment discussing it is possible to highlight that zip is not suitable in my case because we should iterate over all array to find if we have similar id's that are not in the same order.
The following code does work independently by the order of the elements inside the 2 arrays
firstArray = firstArray.map { (item) -> Item in
guard
let index = secondArray.index(where: { $0.id == item.id })
else { return item }
var item = item
item.name = secondArray[index].name
return item
}
"[Item(id: 1, name: "Bogdan", value: 3), Item(id: 2, name: "Max", value: 5)]\n"
Update
The following version uses the first(where: method as suggested by Martin R.
firstArray = firstArray.map { item -> Item in
guard let secondElm = secondArray.first(where: { $0.id == item.id }) else { return item }
var item = item
item.name = secondElm.name
return item
}
A solution for your specific problem above would be:
struct Item {
var id: Int
var name: String
}
let first = Item(id: 1, name: "Oleg")
let second = Item(id: 2, name: "Olexander")
let firstInSecond = Item(id: 1, name: "Bogdan")
let secondInSecond = Item(id: 2, name: "Max")
let ret = zip([first, second], [firstInSecond, secondInSecond]).map({
return $0.id == $1.id ? $1 : $0
})
=> But it requires that there are as many items in the first as in the second array - and that they have both the same ids in the same order...
The map function cannot directly mutate its elements. And since you're using structs (passed by value), it wouldn't work anyway, because the version you see in $0 would be a different instance than the one in the array. To use map correctly, I'd use a closure like this:
fisrtArray = zip(fisrtArray, secondArray).map() {
return Item(id: $0.id, name: $1.name, value: $0.value)
}
This produces the result you're expecting.
Now, if your structs were objects (value types instead of reference types), you could use forEach and do the $0.name = $1.name in there.

Resources