I am using an array that is filled with structs:
struct CartModel: Codable {
var product_id: Int
var line_subtotal: Float
var line_total: Float
var line_tax: Float
var line_subtotal_tax: Float
var key: String
var quantity: Int
}
static var fullCart = [CartModel]()
Now I am trying to delete one of those from the array.
I tried to use fullCart.index(of:), but it is giving me the following error:
Argument labels '(of:, _:)' do not match any available overloads
I was hoping to use fullCart.remove(at:), but because of not being able to find the right index I am not sure how to remove the right item.
You also can find index by index(where:) and then delete item from array:
if let index = fullCart.index(where: { $0.product_id == otherCartModel.product_id }) {
fullCart.remove(at: index)
}
Or conform to Equatable protocol and use index(of:):
struct CartModel: Codable, Equatable {
...
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.product_id == rhs.product_id
}
}
if let index = fullCart.index(of: otherCartModel) {
fullCart.remove(at: index)
}
Related
Normally I have an array with MemoryComponent classes (so [MemoryComponent]). I'd like to refactor that to a custom array class, which has an element type of MemoryComponent and includes stored properties which the program can use.
I tried creating an extension, but it doesn't allow stored properties:
extension Array where Element: MemoryComponent {
// ... no stored properties due to extension
}
I also tried creating another class for the array:
class StorageArray: Array<MemoryComponent> {
// ... stored properties are possible
// but there's an error because of this:
// error: inheritance from non-protocol, non-class type 'Array<MemoryComponent>'
}
How do I effectively create an inheritance from [MemoryComponent] to include stored properties?
It's probably better to use composition for your case.
... but if a custom sort of Array is what you really want, there is actually a weird and not recommended option to hack that.
You can't inherit from Array since it's a struct. But you can implement the Collection protocol.
struct MemoryComponent {}
struct MemoryComponentsArray: Collection {
// typealias all associatedTypes of `Collection` protocol
typealias Element = MemoryComponent
typealias Index = Array<Element>.Index
typealias SubSequence = Array<Element>.SubSequence
typealias Iterator = Array<Element>.Iterator
typealias Indices = Array<Element>.Indices
/// Your real data storage
private let internalArray: Array<Element>
/**
Create any custom initializers you need
*/
init(_ arrayLiteral: [Element]) {
self.internalArray = arrayLiteral
}
// Implement `Collection` protocol core stuff
// By referencing to internal array
var startIndex: Index { internalArray.startIndex }
var endIndex: Index { internalArray.endIndex }
func makeIterator() -> Iterator { internalArray.makeIterator() }
subscript(position: Index) -> Element { internalArray[position] }
subscript(bounds: Range<Index>) -> SubSequence { internalArray[bounds] }
var indices: Indices { internalArray.indices }
var isEmpty: Bool { internalArray.isEmpty }
var count: Int { internalArray.count }
func index(_ i: Index, offsetBy distance: Int) -> Index {
internalArray.index(i, offsetBy: distance)
}
func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? {
internalArray.index(i, offsetBy: distance, limitedBy: limit)
}
func distance(from start: Index, to end: Index) -> Int {
internalArray.distance(from: start, to: end)
}
func index(after i: Index) -> Index {
internalArray.index(after: i)
}
func formIndex(after i: inout Index) {
internalArray.formIndex(after: &i)
}
}
How we can achieve this Filter in Swift.
I have exactly same problem and i am trying this way and i found this solution on stack overflow
but this is written in Javascript and i need code in Swift language.
Getting this error
Cannot convert value of type '[Model]' to closure result type
'GetModel'
My Code and Model
extension Array where Element == GetModel{
func matching(_ text: String?) -> [GetModel] {
if let text = text, text.count > 0{
return self.map{
$0.data.filter{
$0.name.lowercased().contains(text.lowercased())
}
}
}else{
return self
}
}
}
// MARK: - GetModel
struct GetModel: Codable {
let id: Int
let name: String
var data: [Model]
}
// MARK: - Model
struct Model:Codable {
let id: Int
let name: String
var isSelected: Bool? = nil
}
You are making two mistakes. First you are using map but you should be using filter. Second you are using filter when you should be using contains(where:). Note you can. use localizedStandardCompare instead of lowercasing your string.
Note: You shouldn't check if your string count is greater than zero. String has an isEmpty property exactly for this purpose.
To check whether a collection is empty, use its isEmpty property
instead of comparing count to zero. Unless the collection guarantees
random-access performance, calculating count can be an O(n) operation.
extension RangeReplaceableCollection where Element == GetModel {
func matching(_ text: String?) -> Self {
guard let text = text, !text.isEmpty else { return self }
return filter { $0.data.contains { $0.name.localizedStandardContains(text) } }
}
}
edit/update:
If you need to filter your GetModal data:
extension RangeReplaceableCollection where Element == GetModel, Self: MutableCollection {
func matching(_ text: String?) -> Self {
guard let text = text, !text.isEmpty else { return self }
var collection = self
for index in collection.indices {
collection[index].data.removeAll { !$0.name.localizedStandardContains(text) }
}
collection.removeAll(where: \.data.isEmpty)
return collection
}
}
I'm continuing my journey to discover the love and hate for Swift's generics, and in the end, I'm still struggling with a fundamental flaw that I can't get around: when storing a generic in an array (even with fancy type erasure), I still need to explicitly cast the resulting value in the array to a known type before I can get or set properties in the array.
I know the type, but the only way I can seem to store it, is by the string name of the class (since you can't make an array of types it seems). Maybe there is a proper way to code/decode the type so it can be stored in an array? I tried with NSClassFromString, but didn't get very far.
Here is a playground that illustrates the challenge:
enum Apple: String {
case braeburn
case macintosh
case honeycrisp
}
protocol AppleProtocol {
var brand: Apple { get set }
}
protocol AppleGetter {
func getApple<T>(for key: Apple) -> T?
}
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
return m.children.first { $0.label == key }?.value
}
}
struct GenericApple<T: Equatable>: AppleProtocol, Hashable {
static func == (lhs: GenericApple<T>, rhs: GenericApple<T>) -> Bool {
return lhs.brand == rhs.brand
}
var hashValue: Int { return brand.hashValue }
var brand: Apple
var generic: T
init(brand: Apple, generic: T) {
self.brand = brand
self.generic = generic
}
}
struct Apples {
typealias Braeburn = GenericApple<Int>
var braeburn = Braeburn(brand: .braeburn, generic: 10)
typealias Honeycrisp = GenericApple<String>
var honeycrisp = Honeycrisp(brand: .honeycrisp, generic: "A generic")
}
extension Apples: PropertyReflectable {
func getApple<T>(for key: Apple, type: T.Type) -> T? {
return self[key.rawValue] as? T
}
}
This works great!
var applesSet = Apples()
var braeburn = applesSet.getApple(for: Apple.braeburn, type: Apples.Braeburn.self)
braeburn?.generic = 14
print(braeburn?.generic)
But what if I want to do:
struct AppleListElement {
let brand: Apple
let type: String
}
var apples = [AppleListElement]()
apples.append(AppleListElement(brand: .braeburn, type: "\(Apples.Braeburn.self)"))
apples.append(AppleListElement(brand: .honeycrisp, type: "\(Apples.Honeycrisp.self)"))
apples.forEach {
applesSet.getApple(for: $0.brand, type: NSClassFromString($0.type))
}
I am having two arrays coming from web service, I need to find out whether the Array2 has the same objects as Array1.
So, For this I am using below code:
var arr1 = [CustomObject]()
var arr2 = [CustomObject]()
var arr3 = [CustomObject]()
var arr4 = [CustomObject]()
self.arr3 = self.arr1 + self.arr2 //concatenate two arrays
self.arr4 = Array(Set(arr3)) // find out uniq values
// below is the extension
extension Array where Element : Hashable {
var unique: [Element] {
var uniqueValues: [Element] = []
forEach { item in
if !uniqueValues.contains(item) {
uniqueValues += [item]
}
}
return uniqueValues
}
}
But it is showing error on above line "Array(Set(arr3))"
Error Is :- To add value to Set
Try this :
var arr1 = ["A","B","C"]
var arr2 = ["A","B","C"]
if Set(arr1).symmetricDifference(arr2).isEmpty {
print("The Arrays Match")
}
Overview:
In order for the set to store custom class / struct, the custom class / struct needs to to conform to Hashable protocol and indirectly Equatable protocol.
Given below is an example using a struct, you can use a class as well.
Code:
struct CustomObject : Hashable{
var something : Int //It is just an example, this could be any type, but some how you should find a way to compute the hash value.
//MARK: Hashable
var hashValue: Int {
return something
}
}
//MARK: CustomObject - Equatable
func ==(lhs: CustomObject, rhs: CustomObject) -> Bool {
return lhs.something == rhs.something
}
Here is the code:
#IBAction func deleteMeme(sender: UIBarButtonItem) {
if let foundIndex = MemeRepository.sharedInstance.memes.indexOf(selectedMeme) {
//remove the item at the found index
MemeRepository.sharedInstance.memes.removeAtIndex(foundIndex)
navigationController?.popViewControllerAnimated(true)
The error happens at the .indexOf method at (selectedMeme).
Cannot convert value of type Meme! to expected argument type #noescape (Meme) throws -> Bool
Meme! is a struct for my app. How do I work through this?
struct Meme {
var topText : String!
var bottomText: String!
var image: UIImage!
var memedImage: UIImage!
init(topText: String, bottomText: String, image: UIImage, memedImage: UIImage) {
self.topText = topText
self.bottomText = bottomText
self.image = image
self.memedImage = memedImage
The error message is misleading. What you actually need is to provide the compiler a way to compare two Meme instances and decide upon which criteria those instances are equal.
Let's say you want two instances having the same name property to be treated as equal.
We make the struct conform to Equatable and we also create an == operator that compares two structs by their name property:
struct Meme:Equatable {
var name:String!
}
func ==(lhs: Meme, rhs: Meme) -> Bool {
return lhs.name == rhs.name
}
Now we can use indexOf with a Meme instance:
let doge = Meme(name: "doge")
let lolcat = Meme(name: "lolcat")
let memes = [lolcat, doge]
if let dogeIndex = memes.indexOf(doge) {
print(dogeIndex) // 1
}
If you wanted to compare two instances not by their name property but by their uniqueness, then you would have to make the struct conform to Hashable and use a unique hashValue property returning an Int:
struct Meme:Hashable {
var name:String!
var hashValue: Int {
return self.name.hashValue
}
init(name: String) {
self.name = name
}
}
func ==(lhs: Meme, rhs: Meme) -> Bool {
return lhs.hashValue == rhs.hashValue
}
let doge = Meme(name: "doge")
let lolcat = Meme(name: "lolcat")
let memes = [lolcat, doge]
if let dogeIndex = memes.indexOf(doge) {
print(dogeIndex) // 1
}
In this example the hashValue is made from self.name, so two different instances of Meme with a same name property will be considered equal. If you don't want that, use another source for the hash value.
Note: in Swift 3, indexOf has become index(of:), so for this example we would change memes.indexOf(doge) to memes.index(of: doge).
If you want to put the comparison inside the indexOf method itself, do it like this:
if let foundIndex = MemeRepository.sharedInstance.memes.indexOf({
UIImagePNGRepresentation($0.memedImage) == UIImagePNGRepresentation(selectedMeme.memedImage)})
Probably not the best way to compare images. If you know the images are the same object, you can use:
.indexOf({$0.memedImage == selectedMeme.memedImage})
but if you want to compare them pixel by pixel or compare the same image scaled to different sizes, that is a little more complicated.