Check if multi dictionary contains a value Swift 4 - arrays

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

Related

Searching and Editing Values in Swift Array or Dictionary

I have a method which is supposed to return a Set of Strings. Here is a method description:
Returns: 10 product names containing the specified string.
If there are several products with the same name, producer's name is added to product's name in the format "<producer> - <product>",
otherwise returns simply "<product>".
Can't figure out how to check if there are duplicate names in the array and then edit them as required
What I've got so far:
struct Product {
let id: String; // unique identifier
let name: String;
let producer: String;
}
protocol Shop {
func addNewProduct(product: Product) -> Bool
func deleteProduct(id: String) -> Bool
func listProductsByName(searchString: String) -> Set<String>
func listProductsByProducer(searchString: String) -> [String]
}
class ShopImpl: Shop {
private var goodsInTheShopDictionary: [String: Product] = [:]
func addNewProduct(product: Product) -> Bool {
let result = goodsInTheShopDictionary[product.id] == nil
if result {
goodsInTheShopDictionary[product.id] = product
}
return result
}
func deleteProduct(id: String) -> Bool {
let result = goodsInTheShopDictionary[id] != nil
if result {
goodsInTheShopDictionary.removeValue(forKey: id)
}
return result
}
func listProductsByName(searchString: String) -> Set<String> {
var result = Set<String>()
let searchedItems = goodsInTheShopDictionary.filter{ $0.value.name.contains(searchString) }
let resultArray = searchedItems.map{ $0.value }
result = Set(searchedItems.map{ $0.value.name })
if result.count > 10 {
result.removeFirst()
}
return result
}
}
If you want to achieve this you would need to iterate over you resultArray and save producer and product into another array. On each iteration you would need to check if the array allready contains either the product name itself or an allready modified version.
A possible implementation would look like this:
var result = [(producer: String, product: String)]()
// iterate over the first 10 results
for item in resultArray.prefix(10){
if let index = result.firstIndex(where: { _ , product in
product == item.name
}){
// the result array allready contains the exact product name
// so we need to convert the name allready in the list
let oldProduct = (producer: result[index].producer, product: "\(result[index].producer) \(result[index].product)")
result[index] = oldProduct
// add the new one
result.append((producer: item.producer, product: "\(item.producer) \(item.name)"))
}
else if !result.filter({ $0.product.components(separatedBy: " ").contains(item.name)}).isEmpty {
// if the result array allready contains a modified version of the name
result.append((producer: item.producer, product: "\(item.producer) \(item.name)"))
} else{
// if the result array does not contain the product yet
result.append((producer: item.producer, product: "\(item.name)"))
}
}
let productNames = result.map{ $0.product}
Please be aware: As you are using a [String: Product], which is a unsorted dictionary, to hold your values this will yield different results (if the resultArray collection is larger than 10) each time you search.
Tested with searchString = name1:
var goodsInTheShopDictionary: [String: Product] = Dictionary(uniqueKeysWithValues: (0...20).map { index in
("\(index)",Product(id: "", name: "name\(index)", producer: "producer\(index)"))
})
goodsInTheShopDictionary["100"] = Product(id: "11", name: "name1", producer: "producer11")
goodsInTheShopDictionary["101"] = Product(id: "12", name: "name1", producer: "producer12")
Result:
["name13", "producer12 name1", "name10", "name19", "producer11 name1",
"name17", "name14", "name18", "producer1 name1", "name16"]

Swift sorting four different type object with same property in array (AnyObject)

class ComparedObject {
let id: String
let order: Int
...
}
class Object1 {
let id: String
let name: String
...
}
class Object2 {
let id: String
let x: Float
...
}
class Object3 {
let id: String
let y: Int
...
}
class Object4 {
let id: String
let z: Double
...
}
var comparedList: [ComparedObject] = [ComparedObject, ComparedObject, ComparedObject, ComparedObject]
var list = [AnyObject] = [Object2, Object1, Object3, Object4]
I have four classes, all with id: String attribute.
And I want to store them into one (AnyObject) array to show in UICollectionView.
So how can I sort list array with comparedList id attributes?
comparedList is already sorted.
I want sort list array from comparedList id attribute:
var comparedList = [
ComparedObject(id: "01d7da417657", order: 0),
ComparedObject(id: "61a96d769843", order: 1),
ComparedObject(id: "5047abd36432", order: 2),
ComparedObject(id: "3213f37a4003", order: 3)
]
var list = [
Object2(id: "3213f37a4003", x: 34.0),
Object1(id: "61a96d769843", name: "name"),
Object3(id: "01d7da417657", y: 9),
Object4(id: "5047abd36432", z: 0.3)
]
To this:
var list = [
Object3(id: "01d7da417657", y: 9),
Object1(id: "61a96d769843", name: "name"),
Object4(id: "5047abd36432", z: 0.3),
Object2(id: "3213f37a4003", x: 34.0)
]
Finally I did it like this:
comparedList = comparedList.sorted { $0.order < $1.order }
var comparedOrder = comparedList.map { $0.id }
list.sort { (comparedOrder.firstIndex(of: $0.id)! < comparedOrder.firstIndex(of: $1.id)! }
You just need to create a protocol that defines a requirement for an id: String property, then make all your types conform to said protocol and instead of storing them as AnyObject, store them as the protocol type. This will enable you to sort the array based on id.
protocol MyIdentifiable {
var id: String { get }
}
struct ComparedObject: MyIdentifiable {
let id: String
}
struct Object1: MyIdentifiable {
let id: String
let name: String
}
struct Object2: MyIdentifiable {
let id: String
let x: Float
}
struct Object3: MyIdentifiable {
let id: String
let y: Int
}
struct Object4: MyIdentifiable {
let id: String
let z: Double
}
let comparedList = [ComparedObject(id: "!")]
let list: [MyIdentifiable] = [Object1(id: "1", name: "1"), Object2(id: "2", x: 2.0)]
let sortedList = list.sorted(by: { $0.id < $1.id })

How to generic a function where params are different structs with different properties?

Please refer the following code:
import UIKit
struct Item {
var brandId = 1
var name: String = ""
}
struct Store {
var areaName = ""
var name: String = ""
}
let itemArray = [Item(brandId: 1, name: "item1"), Item(brandId: 2, name: "item2"), Item(brandId: 1, name: "item3") ]
let storeArray = [Store(areaName: "hk", name: "store1"), Store(areaName: "bj", name: "store2"), Store(areaName: "hk", name: "store3")]
var intKeys = [Int]()
var groupedItems = [[Item]]()
var stringKeys = [String]()
var groupedStores = [[Store]]()
extension Array {
func transTo2d() -> [[Element]] {
let grouped = [[Element]]()
return grouped
}
}
itemArray.forEach { (item) in
let brandId = item.brandId
if !intKeys.contains(brandId) {
intKeys.append(brandId)
var newArray = [Item]()
newArray.append(item)
groupedItems.append(newArray)
} else {
let index = intKeys.index(of: brandId)!
groupedItems[index].append(item)
}
}
My final goal is could using itemArray.transTo2d() get a 2d array based on item's brandId, using storeArray.transTo2d() get a 2d array based on store's areaName. I don't how to generic the function that trans 1d array to a 2d array based on the key?
I don't think you can write a generic extension for an Array where the elements will either be of type Item or Store since both of them don't share any relation for you to write a common generic method. You can write extensions for Array where the elements will be of the mentioned type. You just need to conform both of your structs to the equatable protocol.
struct Item {
var brandId = 1
var name: String = ""
}
extension Item : Equatable{
static func ==(lhs: Item, rhs: Item) -> Bool{
return lhs.brandId == rhs.brandId
}
}
struct Store {
var areaName = ""
var name: String = ""
}
extension Store : Equatable{
static func ==(lhs: Store, rhs: Store) -> Bool{
return lhs.areaName == rhs.areaName
}
}
extension Array where Element == Store{
func transform()->[[Store]]{
var storeArray = self
var groupedArray = [[Store]]()
while storeArray.count > 0{
if let firstElement = storeArray.first{
groupedArray.append(storeArray.filter{$0.areaName == firstElement.areaName})
storeArray = storeArray.filter{$0.areaName != firstElement.areaName}
}
}
return groupedArray
}
}
extension Array where Element == Item{
func transform()->[[Item]]{
var itemArray = self
var groupedArray = [[Item]]()
while itemArray.count > 0{
if let firstElement = itemArray.first{
groupedArray.append(itemArray.filter{$0.brandId == firstElement.brandId})
itemArray = itemArray.filter{$0.brandId != firstElement.brandId}
}
}
return groupedArray
}
}
Using the transform function
let storeArray = [Store(areaName: "hk", name: "store1"), Store(areaName: "bj", name: "store2"), Store(areaName: "hk", name: "store3")]
let itemArray = [Item(brandId: 1, name: "item1"), Item(brandId: 2, name: "item2"), Item(brandId: 1, name: "item3") ]
print(storeArray.transform())
print(itemArray.transform())
This will print this output which is what I believe you wanted.
[[Store(areaName: "hk", name: "store1"), Store(areaName: "hk", name: "store3")], [Store(areaName: "bj", name: "store2")]]
[[Item(brandId: 1, name: "item1"), Item(brandId: 1, name: "item3")], [Item(brandId: 2, name: "item2")]]

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

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.

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

Resources