I have a protocol, structure and instances + protocol and class, which should change the instances of the structure:
protocol Cars {
var car: String { get set }
var accesories: [String] { get set }
}
struct Car: Cars {
var car: String
var accesories: [String]
}
var carOne = Car(car: "Car One", accesories: ["accessoryOne", "accessoryTwo"])
var carTwo = Car(car: "Car Two", accesories: ["accessoryTwo"])
protocol Dealership {
static var cars: [Car] { get set }
static func addAccesories(add: [String])
}
Next, in DealershipOne I want to make a func addAccesories that will add an array of strings to the instance property, I try do it this way and some other ways, but I get the error Cannot use mutating member on immutable value: '$0' is a 'let' constant.
class DealershipOne: Dealership {
static var cars = [carOne, carTwo]
static func addAccesories(add: [String]) {
cars.forEach{ $0.accesories.append(contentsOf: add) } // ERROR
}
}
How do I resolve this?
You can not add new value to direct iterated objects. $0 is a let object so you can not add value.
A solution to use index and update the value.
cars.indices.forEach{ cars[$0].accesories.append(contentsOf: add) }```
The anonymous closure parameter ($0) within your forEach loop is an immutable copy of a car from the cars you’re trying to iterate.
You could make a mutable copy with var car = $0, but even that won’t help, because you would be mutating the copy, and not the actual cars in the static cars array.
If you would like to keep using struct to model your cars, then the only way to modify properties of those cars within the array is to get an l-value (in c++ lingo) via assignment through a subscript:
func add(accessories: [String]) {
for i in cars.insides {
cars[i].accessories += accessories
}
}
(I also made some minor tweaks to make this more idiomatic, such as t changing the function name, and using the += operator for arrays instead of Array.append(contentsOf:))
Related
var brachNames = ["AP","AP","AP","AS","AS","AS","BR","BR","BR"]
var overAllTaget = ["84","84","84","84","84","84","84","84","84"]
var overAllSold = ["135","135","135","135","135","135","135","135","135"]
extension Array where Element : Hashable {
func removeDups() -> [Element] {
var uniquedElements = Set<Element>()
return filter { uniquedElements.insert($0).inserted }
}
}
I want this type of output - [AP,84,135,AS,84,135,BR,84,135]
Since you've 3 different Arrays, you need to first combine these to get an Array of Arrays using zip(_:_:) and map(_:), i.e.
var arr = zip(brachNames, zip(overAllTaget, overAllSold)).map { [$0.0, $0.1.0, $0.1.1] }
Now, use Set to filter out the duplicates. Then use flatMap(_:) to get a single result Array, i.e.
let result = Array(Set(arr)).flatMap{ $0 } //["AP", "84", "135", "AS", "84", "135", "BR", "84", "135"]
Note: Set is unordered. So, the sequence of the result might change.
Another approach would be to create a struct with the required fields (brachName, overallTarget, overallSold), comply to Hashable and apply something like this:
https://www.hackingwithswift.com/example-code/language/how-to-remove-duplicate-items-from-an-array
This way you could keep the order, if that's important.
It would be much better to work with an array of a custom type instead of 3 different arrays of data to make the code clearer and to avoid simple mistakes when accessing the data. Below is an example of such solution using a struct to hold the data
struct BranchData: Hashable {
let branchName: String
let overallTarget: Int
let overallSold: Int
}
var set = Set<BranchData>()
for (index, branch) in brachNames.enumerated() {
guard index < overAllSold.count, index < overAllTaget.count else {
break
}
set.insert(BranchData(branchName: branch, overallTarget: Int(overAllTaget[index]) ?? 0, overallSold: Int(overAllSold[index]) ?? 0))
}
To support the specific output with all values in an array we can add a computed property
extension BranchData {
var propertyArray: [String] {
[branchName, String(overallTarget), String(overallSold)]
}
}
let output = set.flatMap { $0.propertyArray }
or a more direct approach
let output = set.flatMap { [$0.branchName, $0.overallTarget, $0.overallSold] }
I am taking some inspiration from
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.
I would like the implementation to be as type safe as possible, however should be reusable.
The strategy that I am using is declaring a Weak Reference container as so.
class WeakRefContainer<T> where T: AnyObject {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
Then I want to maintain an array of these WeakRefContainers, so I create an array extension:
extension Array where Element: WeakRefContainer<AnyObject> {
func compact() -> [WeakRefContainer<AnyObject>] {
return filter { $0.value != nil }
}
}
When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.
I am now having some compilation issues which am having trouble understanding.
Lets suppose I have a sample class
class SampleClass {
}
And I try to use everything as follows:
var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)
weakReferencesArray.compact()
When I try to call compact I get the following error message:
MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'
Can anyone unblock me please? Thanks
Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.
There is a workaround for this, via a protocol:
protocol WeakHolder {
var hasRef: Bool { get }
}
extension WeakRefContainer: WeakHolder {
var hasRef: Bool { return value != nil }
}
extension Array where Element: WeakHolder {
func compacted() -> [Element] {
return filter { $0.hasRef }
}
mutating func compact() {
self = compacted()
}
}
I also renamed compact to compacted, for better Swift semantics, and replaced the original compact by a mutating version.
You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject.
extension Array where Element: WeakRefContainer<T> {
However, currently, parameterised extensions are not possible. See this proposal.
You can kind of work around this by making compact generic:
extension Array{
func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
return filter { $0.value != nil }
}
}
I'm passing an array of a specific model by reference between ViewControllers.
If I change any value of a specific element in the array it reflects well in all ViewControllers but when I remove an element from that array it doesn't reflect to the other controllers.
Does the remove(at: ) function create new array and refer to another address?
And if so how to delete an element without changing the address of array so it can reflect this change on the other view controllers?
Swift Arrays are value types (specifically, an array is a struct), not reference types, so you are mistaken when you say that you are "passing an array of a specific model by reference between view controllers". You can only ever pass a Swift array as a value.
Arrays, like other structs, have copy-on-modify semantics. As soon as you change the array itself a copy is made and the change is made to the copy.
Now, in your case the array contains references to model objects; When you update the model object you change the object itself, not the reference held in the array, so you see the change reflected in all of your view controllers.
An analogy might be the difference between adding a house to a street (which changes the street itself) versus changing the occupants of an existing house on the street.
I would suggest you implement a model object that provides abstraction from the underlying array so that you have better code and avoid the issue with array references.
One approach could be something like:
struct MyModel {
let name: String
let size: Int
}
class MyData {
private var _models = [MyModel]()
var models: [MyModel] {
return _models
}
func insert(model: MyModel) {
self._models.append(model)
}
func removeModel(at: Int) {
guard at >= 0 && at < _models.count else {
return
}
self._models.remove(at: at)
}
}
Although this isn't ideal as it still requires model consumers to know indices in the underlying array. I would prefer something like this:
struct MyModel: Hashable {
let name: String
let size: Int
}
class MyData {
private var _models = [MyModel]()
var models: [MyModel] {
return _models
}
func insert(model: MyModel) {
self._models.append(model)
}
func remove(model: MyModel) -> Bool {
if let index = self._models.index(of: model) {
_models.remove(at: index)
return true
} else {
return false
}
}
}
Now I don't need to know what internal collection MyData uses to store the models.
If you need to pass an array (or any other value type) by reference, you could go through an intermediate structure that manages the indirection for you.
[EDIT] changed to use KeyPaths available in Swift 4.
// Generic class to hold a "weak" reference to a property from an object
// including properties that are valued types such as arrays, structs, etc.
// This is merely an encapsulation of Swift's native KeyPath feature
// to make the code a bit more readable and simpler to use
//
class ReferenceTo<ValueType> { var value:ValueType! { get { return nil} set {} } }
class Reference<OwnerType:AnyObject,ValueType>:ReferenceTo<ValueType>
{
internal weak var owner:OwnerType!
internal var property:ReferenceWritableKeyPath<OwnerType,ValueType>! = nil
internal var valueRef:KeyPath<OwnerType,ValueType>! = nil
init(_ owner:OwnerType, _ property:ReferenceWritableKeyPath<OwnerType,ValueType>)
{ (self.owner,self.property) = (owner,property) }
init(_ owner:OwnerType, get valueRef:KeyPath<OwnerType,ValueType>)
{ (self.owner,self.valueRef) = (owner,valueRef) }
override var value:ValueType!
{
get { return valueRef != nil ? owner?[keyPath:valueRef] : owner?[keyPath:property] }
set { owner?[keyPath:property] = newValue }
}
}
With this generic class you can create references to valued type properties of object instances and manipulate them anywhere in your code as if the valued type property was a reference type.
// Example class with a read/write and a read-only property:
class MyObject
{
var myArray = [1,2,3,4]
var total:Int { return myArray.reduce(0,+) }
}
var instance:MyObject! = MyObject()
// create a reference to the array (valued type)
// that can be used anywhere and passed around as a parameter
let arrayRef = Reference(instance, \.myArray)
// the value is accessed and manipulated using the
// "value" property of the reference
arrayRef.value.remove(at:2)
arrayRef.value.append(5)
print(instance.myArray) // [1,2,4,5]
// Read-only properties can also be manipulated as
// references
let valueRef = Reference(instance, get:\.total)
print(valueRef.value) // 12
The Reference class allows passing the value as a reference to function parameters
// a function that expects a reference to an array
// would be declared as follows
func changeArray(_ array:ReferenceTo<[Int]>)
{ array.value.insert(9, at: 1) }
// the reference can also be used as an inout parameter
func shift(_ array:inout [Int])
{ array = Array(array.dropFirst()) + Array(array.prefix(1)) }
changeArray(arrayRef)
shift(&arrayRef.value!)
print(instance.myArray) // [9,2,4,5,1]
...
// the reference uses a weak link to the owner
// of the referenced property or value
// so there will be no strong reference cycle issues even
// if the reference is used in an object held strongly
// by the owner itself
instance = nil
print(arrayRef.value) // none ... no more value after the owner is gone
I'm trying to create an extension of Array that returns a new array of unique items based on the items with the closure applied.
For example: If I had an array of Apple where apple has properties name and origin, to get one Apple of each origin I would call apple.uniqued(on: { $0.origin })
Here's the code I have so far:
extension Array where Element: Equatable {
func uniqued(on extract: (Element) -> Equatable) { // A
let elementsAndValues = map { (item: $0, extracted: extract($0)) } // 1
var uniqueValues: [Element] = []
var uniqueExtracts: [Equatable] = [] // A
elementsAndValues.forEach { (item, extracted) in
if !uniqueExtracts.contains(extracted) { // 3, B
uniqueValues += [item]
uniqueExtracts += [extracted]
}
}
return uniqueValues
}
}
This should work as follows:
Create an array of tuples with both the original elements (item) and the elements with the closure applied (extracted)
If uniqueExtracts doesn't contain the item, add it and add the item to the uniqueItems array.
The errors I'm getting are:
A) "Protocol 'SomeProtocol' can only be used as a generic constraint because it has Self or associated type requirements" (twice)
B) "Missing argument label 'where:' in call"
I'm using the latest version of Xcode. Any advice would be a lot of help. Many thanks.
You have multiple problems that mix together to create the errors you’re seeing. What you should do is use a generic.
extension Array
{
func uniqued<T:Equatable>(on extract:(Array.Element) -> T) -> [Array.Element]
{
let elementsAndValues = self.map{ (item: $0, extracted: extract($0)) }
var uniqueValues:[Element] = []
var uniqueExtracts:[T] = []
for (item, extracted) in elementsAndValues
{
if !uniqueExtracts.contains(extracted)
{
uniqueValues.append(item)
uniqueExtracts.append(extracted)
}
}
return uniqueValues
}
}
The <T:Equatable> declares a generic type parameter T that conforms to Equatable. Then the function signature can expect a closure that returns some generic type T that we know conforms to Equatable from the type constraint in the angle brackets. You also have to change every occurrence of Equatable to the generic parameter T, since Equatable isn’t a real type; see my answer here. If you do that the code should compile.
You have a few other things you should probably change:
Instead of using elementsAndValues.forEach(:), use a for <pattern> in list {} loop.
Although this is contentious, you should probably use the Array().append(:) method instead of += concatenation when adding one element to an array. In the case of +=, as opposed to +, this is purely to convey intent.
You did not declare a return type for your function, so the compiler assumes it returns Void, and so the return uniqueValues statement will cause a compiler error. Add a -> [Array.Element] to the function to fix this.
where Element:Equatable as a constraint on Array itself is superfluous. You are using a key function to determine equability, so whether the elements themselves are equatable is irrelevant.
You may want to use a Set or some other hashed data structure instead of a uniqueExtracts array. Testing for membership in an array is an O(n) operation.
I would do this with a group(by:) function, which would group each element in the sequence by a given key (e.g. the origin), yielding a Dictionary mapping keys to groups (arrays of elements in the group). From there, I would just map over the dictionary and just get the first element in each group.
public extension Sequence {
public typealias Element = Iterator.Element
public typealias Group = [Element]
public func group<Key: Hashable>(by deriveKey: (Element) -> Key) -> [Key: Group] {
var groups = [Key: Group]()
for element in self {
let key = deriveKey(element)
if var existingArray = groups[key] { // Group already exists for this key
groups[key] = nil //performance optimisation to prevent CoW
existingArray.append(element)
groups[key] = existingArray
}
else {
groups[key] = [element] // Create new group
}
}
return groups
}
}
struct Apple {
let name: String
let origin: String
}
let apples = [
Apple(name: "Foo", origin: "Origin 1"),
Apple(name: "Bar", origin: "Origin 1"),
Apple(name: "Baz", origin: "Origin 2")
]
let firstAppleInEachOrigin = apples.group(by: {$0.origin}).flatMap{ _, group in group.first }
firstAppleInEachOrigin.forEach{ print($0) }
I'm attempting to customize the "Page-Based Application" template and am new to Swift (and all programming for that matter). In my ModelController.swift I've created a 2D array to use as the primary datasource.
However, when I go to print it or use it as label.text it is including the parenthesis. How do I get just the strings of the array element I'm calling for?
class ModelController: NSObject, UIPageViewControllerDataSource {
var pageData: NSArray = [[String]]()
override init() {
super.init()
// Create the data model.
pageData = [["right", "wrong"], ["left", "right"]]
println(pageData[1]) //For
}
I'm not sure what else from this file is relevant but here's another bit.
func indexOfViewController(viewController: DataViewController) -> Int {
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
if let dataObject: AnyObject = viewController.dataObject {
return self.pageData.indexOfObject(dataObject)
} else {
return NSNotFound
}
}
If you just want left, right
println("\(pageData[1][0]), \(pageData[1][1])")
If you literally want "left, right" with the double quotes included it's
println("\"\(pageData[1][0]), \(pageData[1][1])\"")
The reason you got the results you did is because pageData[1] isn't a String it's an NSArray. Because NSArray conforms to the Printable protocol it supplies its .description when used in println.