How can i disable adding new item to an array in Swift? - swift

I have an array like this inside a struct:
struct TestType {
private(set) var array: [String] = ["0", "1", "2"]
mutating func updateItem0(_ value: String) {
self.array[0] = value
}
mutating func updateItem1(_ value: String) {
self.array[1] = value
}
mutating func updateItem2(_ value: String) {
self.array[2] = value
}
}
I want be able to disable appending method to this array when I use an instance of struct, and keeping the count of it as it is. I cannot use private(set) because it would not allow me to update items of it.
My idea is using private(set) inside struct and making a mutating function for updating items in case, I wanted to know if there is better way for it?

Lots of options but a simple enhancement would be passing the index:
mutating func update(_ value: String, at index: Int) {
array[index] = value
}
And another is to check if the operation is possible:
enum Error: Swift.Error {
case indexOutOfBound
}
mutating func update(_ value: String, at index: Int) throws {
guard array.indices.contains(index) else { throw Error.indexOutOfBound }
array[index] = value
}

Here is a nice way to handle it. Add subscript to your struct which then allows you to access and change the values like you would an array. Adopting CustomStringConvertible and implementing description allows you to print the internal array while keeping it entirely private:
struct TestType: CustomStringConvertible {
private var array: [String] = ["0", "1", "2"]
var description: String { String(describing: array) }
subscript(_ index: Int) -> String {
get {
return array[index]
}
set {
array[index] = newValue
}
}
}
var foo = TestType()
foo[0] = "hello"
foo[2] = "goodbye"
foo[3] = "oops" // Fatal error: Index out of range
print(foo[0]) // hello
print(foo[1]) // 1
print(foo[2]) // goodbye
print(foo) // ["hello", "1", "goodbye"]

Related

How to filter an Actor object?

I have an Actor object that I want to be able to iterate and filter.
actor DataModel {
typealias Details = (passed: Bool, scores: [Int])
private(set) var data: [Int: Details] = [:]
func update(_ value: (Bool, [Int]), forKey key: Int) {
data.updateValue(value, forKey: key)
}
subscript(id: Int) -> Details? {
get {
data[id]
}
set {
data[id] = newValue
}
}
func removeAll() {
data.removeAll()
}
}
extension DataModel: AsyncSequence, AsyncIteratorProtocol {
typealias Element = (key: Int, value: Details)
func next() async throws -> Element? {
var iterator = data.makeIterator()
return iterator.next()
}
nonisolated func makeAsyncIterator() -> Data {
self
}
}
let data = DataModel()
await data.update((false, [1, 2]), forKey: 0)
But, whenever I use the filter method, it goes into an infinite loop.
let filtered = data.filter { el in
/// infinite loop
return el.value.passed || el.value.scores.count > 3
}
for try await i in filtered {
print(i)
}
Update
Created a separate iterator, but getting the following error:
Actor-isolated property 'data' can not be referenced from a non-isolated context
extension DataDetail: AsyncSequence {
typealias Element = (key: Int, value: (passed: Bool, scores: [Int]))
typealias AsyncIterator = DataInterator
nonisolated func makeAsyncIterator() -> DataInterator {
return DataInterator(data) /// Actor-isolated property 'data' can not be referenced from a non-isolated context
}
}
struct DataInterator: AsyncIteratorProtocol {
typealias Detail = (key: Int, value: (passed: Bool, scores: [Int]))
private let details: [Int: (passed: Bool, scores: [Int])]
lazy var iterator = details.makeIterator()
init(_ details: [Int: (passed: Bool, scores: [Int])]) {
self.details = details
}
mutating func next() async throws -> Detail? {
let nextDetail = iterator.next()
return nextDetail
}
}
You have a mistake in your next() method. You're creating a new iterator on each call, so every call to your next() method is effectively returning data.first over and over again. It'll never hit nil, so it'll never end.
I'm not sure what the easiest way to fix it is, however. You can't just return data.makeIterator() from makeAsyncIterator(), because data is actor-isolated.
You'll probably want to make a new AsyncIteratorProtocol-conforming struct which wraps your actor and vends the elements of its data in an actor-isolated way

Redundant duplication of typealiase declarations when conforms to a protocol

protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
associatedtype Target: Collection
static var reference: KeyPath<Self, Target> { get }
}
extension PathCollection {
private var target: Target { self[keyPath: Self.reference] }
var startIndex: Index { target.startIndex }
var endIndex: Index { target.endIndex }
subscript(index: Index) -> Element {
get { target[index] }
}
func index(after i: Index) -> Index {
target.index(after: i)
}
}
It's pretty useful protocol which helps us to reduce boilerplate code when creating custom collections.
Suppose our struct wraps a dictionary. And we want it to be a collection just like that dictionary.
We should provide keyPath to the dictionary property and apply to the protocol. And it works!
Example of usage and my question:
protocol Graph: PathCollection where Target == [String: Int] {
var storage: [String: Int] { get set }
}
extension Graph {
static var reference: KeyPath<Self, [String: Int]> { \.storage }
}
struct UndirectedGraph: Graph {
typealias Element = Dictionary<String, Int>.Element // Why should we again declare this typealias!?
typealias Index = Dictionary<String, Int>.Index // Why should we again declare this typealias!?
var storage: [String: Int]
}
It perfectly works. But why should we redeclare Element and Index typealiases!? At the very first line of code of this post we explicitly defines Element and Index:
protocol PathCollection: Collection where Element == Target.Element, Index == Target.Index {
and then:
protocol Graph: PathCollection where Target == [String: Int] {
If I remove that redeclarations I get an compilation error, which I don't understand:
'PathCollection' requires the types 'Slice' and
'Dictionary<String, Int>.Element' (aka '(key: String, value: Int)') be
equivalent

How can I implement a CountedSet (NSCountedSet) in Swift?

Create a generic CountedSet struct that is constrained to Hashable elements. A counted set is an unordered collection of unique elements that may appear more than once in the collection. Use a private dictionary as your backing storage for set members and their counts.
struct CountedSet<Element> {
private(set) var elements: [Element]
mutating func insert(_ element: Element) {
elements.append(element)
}
mutating func remove() -> Element? {
guard elements.isEmpty == false else { return nil}
return elements.removeFirst()
}
subscript(_ member: Element) -> Int {
return 0
}
}
I don't understand what the real objective is here. The instructions are very confusing at least to me.
1) Make your generic struct element conform to Hashable, this is necessary because the dictionary keys are required to conform to Hashable.
struct CountedSet<Element: Hashable>
2) The backing storage you have used is an ordered array, not a dictionary and you need to initialize it with an empty one.
private(set) var elements: [Element: Int] = [:]
3) Your subscript method you need to return the count for the counted set member or zero if it is nil.
return elements[member] ?? 0
4) Your Insert and Remove methods need to first check the count of a member in the backing dictionary before adding or removing an element from it.
So your CountedSet should look like this:
struct CountedSet<Element: Hashable> {
private(set) var elements: [Element: Int] = [:]
mutating func insert(_ member: Element) {
elements[member, default: 0] += 1
}
mutating func remove(_ member: Element) -> Element? {
guard var count = elements[member], count > 0 else { return nil }
count -= 1
elements[member] = count == 0 ? nil : count
return member
}
subscript(_ member: Element) -> Int {
elements[member] ?? 0
}
}
var countedSet = CountedSet<Int>()
countedSet.insert(3)
countedSet.insert(3)
countedSet.insert(4)
countedSet.elements // [4: 1, 3: 2]
countedSet.remove(4)
countedSet.elements // [3: 2]
countedSet.remove(4) // nil
Expanding on that you can also make your CountedSet conform to ExpressibleByArrayLiteral to allow you to initialize your CountedSet with an array and CustomStringConvertible to allow you to print its elements:
extension CountedSet: ExpressibleByArrayLiteral, CustomStringConvertible {
typealias ArrayLiteralElement = Element
init<S: Sequence>(_ sequence: S) where S.Element == Element {
self.elements = sequence.reduce(into: [:]) { $0[$1, default: 0] += 1 }
}
init(arrayLiteral elements: Element...) { self.init(elements) }
var description: String { .init(describing: elements) }
}
var countedSet: CountedSet = [1,2,2,3,3,3,4,4,5,5,5]
print(countedSet) // "[5: 3, 2: 2, 3: 3, 4: 2, 1: 1]\n"

Ambiguous reference to member 'contains'

I have defined a struct for the Stack:
struct Stack<T> {
private(set) var elements = [T]()
var isEmpty: Bool { return elements.isEmpty }
mutating func push(newElement: T) {
elements.append(newElement)
}
mutating func pop() -> T {
return elements.removeLast()
}
func top() -> T? {
return elements.last
}
}
When I use the method to get the last element and check if it is in the collection:
if operators.contains(stack.top()!) {
//do smth
}
compiler raises an error: "Ambiguous reference to member 'contains'"
Update: The collection is defined as:
struct Operator: OperatorType {
let name: String
let precedence: Int
let associativity: Associativity
// same operator names are not allowed
var hashValue: Int { return "\(name)".hashValue }
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
self.name = name; self.precedence = precedence; self.associativity = associativity
}
}
And init with:
let operators: Set <Operator> = [
Operator("%", 4, .Right),
Operator("*", 3, .Left),
Operator("/", 3, .Left),
Operator("+", 2, .Left),
Operator("-", 2, .Left)
]
What am I doing wrong?
This is because the contains method you are looking for only exists where Array.Element : Equatable
Taken from the stdlib.
extension Array where Element : Equatable {
...
public func contains(_ element: Element) -> Bool
}
Assuming someCollection is Array<Stack> then you would need to change Stack to:
struct Stack<T: Equatable> : Equatable {
And with Swift 4.1 the Equatable conformance is handled automatically. Source

Is it possible to have a range as a key in a Swift Dictionary?

For simplification. Lets say i have some unique values -> the numbers from 1 to 10
Now I want 1-5 map to the value "first" and I want 6-10 map to the value "second"
Is there a way I can create or extend a dictionary to work like the following?
let dict: [Range<Int> : String]
The goal is to have the following results:
print(dict[1]) // prints first
print(dict[2]) // prints first
print(dict[3]) // prints first
print(dict[7]) // prints second
print(dict[8]) // prints second
print(dict[9]) // prints second
The way I am currently doing it is to simply have the multiple keys map to the same value. But my dictionary can have sometimes 60k values. So I am wondering if a range can work.
I know I can make the value into a class instead of a struct so that multiple keys can map to the same class object, but I was wondering if simply creating a Dictionary that worked like above was possible?
If you insist on using Dictionary, you have to wait until Swift 3.1 (currently in beta):
extension CountableClosedRange : Hashable {
public var hashValue: Int {
return "\(lowerBound) to \(upperBound)".hashValue
}
}
// This feature is called concrete-type extension and requires Swift 3.1
extension Dictionary where Key == CountableClosedRange<Int> {
subscript(rawValue rawValue: Int) -> Value? {
for k in self.keys {
if k ~= rawValue {
return self[k]
}
}
return nil
}
}
let dict : [CountableClosedRange<Int>: String] = [
1...5: "first",
6...10: "second"
]
print(dict[rawValue: 1])
print(dict[rawValue: 2])
print(dict[rawValue: 3])
print(dict[rawValue: 7])
print(dict[rawValue: 8])
print(dict[rawValue: 9])
However, it's a lot clearer if you implement your own data model:
struct MyRange {
var ranges = [CountableClosedRange<Int>]()
var descriptions = [String]()
mutating func append(range: CountableClosedRange<Int>, description: String) {
// You can check for overlapping range here if you want
self.ranges.append(range)
self.descriptions.append(description)
}
subscript(value: Int) -> String? {
for (i, range) in self.ranges.enumerated() {
if range ~= value {
return descriptions[i]
}
}
return nil
}
}
var range = MyRange()
range.append(range: 1...5, description: "one")
range.append(range: 6...10, description: "second")
print(range[1])
print(range[2])
print(range[6])
print(range[7])
print(range[100])
This is in Swift 3.0, it may not be as nice as Code Different's answer though.
class MyRange: Hashable, Equatable {
public var hashValue: Int {
get {
return (self.range.lowerBound + self.range.upperBound).hashValue
}
}
var range: Range<Int>!
public static func ==(_ lhs: MyRange, _ rhs: MyRange) -> Bool {
return lhs.range == rhs.range
}
init(range: Range<Int>) {
self.range = range
}
}
extension Dictionary where Key: MyRange, Value: ExpressibleByStringLiteral {
internal subscript(index: Int) -> [String] {
return self.filter({$0.key.range.contains(index)}).map({$0.value as! String})
}
}
Now, you can make your dictionary like so:
var dict = Dictionary<MyRange, String>()
dict[MyRange(range: 0..<5)] = "first"
dict[MyRange(range: 5..<10)] = "second"
Getting values works with Integers and Ranges:
print(dict[1]) // ["first"]
print(dict[5]) // ["second"]
print(dict[11]) // []
print(dict[MyRange(range: 0..<5)]) // "first"
print(dict[MyRange(range: 0..<6)]) // nil
The dictionary should look like this:
print(dict)
// [MyRange: "first", MyRange: "second"]