Override count (or whatever) in a SPECIFIC array - arrays

Say you have
class Blah {
}
and then I'm going to have [Blah]
However, the Blah array is going to work a bit differently from a normal array.
For example, I want count to work like this, say
override count {
c = super.count // an ordinary count of the array
y = count of items where blah.color = yellow
return y
}
Of course, I know how to override count (or whatever) by subclassing the appropriate array concept in Swift.
But how can I override count "only in" the array [Blah] ... is that possible?
Use case - maybe there's a better way - Blah has a number of concrete subtypes A:Blah, B:Blah .. F:Blah I want to filter [Blah] so it only returns certain of them (say, "B and D types only") when you enumerate, and the count, etc, would only be for the turned-on subtypes. I appreciate Swift's slice and so on could be relevant here.

Like people are commenting, you don't really want to override count. Here's some code that shows why that's not going to work and gives another possible solution.
//: Playground - noun: a place where people can play
class Blah {
let include: Bool
init(include: Bool) {
self.include = include
}
}
// This code "works", but you'll get an error that "count" is ambiguous because
// it's defined in two places, and there's no way to specify which one you want
//extension Array where Element: Blah {
//
// var count: Int {
// return reduce(0) { result, element in
// guard element.include else {
// return result
// }
//
// return result + 1
// }
// }
//
//}
// But you could add a helper to any blah sequence that will filter the count for you
extension Sequence where Iterator.Element: Blah {
var includedBlahCount: Int {
return reduce(0) { result, blah in
guard blah.include else {
return result
}
return result + 1
}
}
}
let blahs = [Blah(include: false), Blah(include: true)]
print(blahs.count) // 2
print(blahs.includedBlahCount) // 1

Related

Remove duplicate Items in multiple arraylist swift

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] }

Type 'Bool' does not conform to protocol 'Sequence'

I started learning Swift few weeks ago and in one lesson (Arrays and for .. in loops) I had to make func that counts votes and gives an answer.
So I made this code thinking thats it but this error pops in -> "Type 'Bool' does not conform to protocol 'Sequence'"
here's the code:
func printResults(forIssue: String, withVotes: Bool) -> String {
positive = 0
negative = 0
for votes in withVotes {
if votes == true {
positive += 1
} else {
negative += 1
}
}
return "\(forIssue) \(positive) yes, \(negative) no"
}
The error pops in 4th line with 'withVotes'
There are already some arrays that got Bool type values.
Welcome to learning Swift! You've stumbled across something where the compiler is right, but as a beginner, it's not always evident on what's going on.
In this case, although it's pointing to line 4 as the problem, that's not where you need to fix it. You need to go to the source of the problem, which in this case is line 1, here...
func printResults(forIssue: String, withVotes: Bool) -> String {
Specifically withVotes: Bool. The problem is because of the way you have it written, it's only allowing you to pass in a single boolean. By your question and the rest of your code, you clearly want to pass in several.
To do that, simply make it a bool array, like this... withVotes: [Bool] (Note the square brackets.)
Here's your updated code with the change on line 1, not line 4. Note I also updated the signature and variable names to be more 'swifty' if you will where the focus should always be on clarity:
func getFormattedResults(for issue: String, withVotes allVotes: [Bool]) -> String {
var yesVotes = 0
var noVotes = 0
for vote in allVotes {
if vote {
yesVotes += 1
}
else {
noVotes += 1
}
}
return "\(issue) \(yesVotes) yes, \(noVotes) no"
}
Hope this explains it a little more, and again, welcome to the Swift family! :)
You need to pass in an array like this:
func printResults(forIssue: String, withVotes: [Bool]) -> String {
positive = 0
negative = 0
for votes in withVotes {
if votes == true {
positive += 1
} else {
negative += 1
}
}
return "\(forIssue) \(positive) yes, \(negative) no"
}
The compiler is right. You're trying to iterate through a bool value withVotes which will not work.
The solution is to create an array of bool values. Like following
for i in [true, false, true] {
if i == true { print("true") }
}
Change your parameter withVotes from Bool to [Bool] and the compiler will be happy :)
At the end and probably will look like that
func printResults(forIssue: String, withVotes: [Bool]) -> String {
positive = 0
negative = 0
for votes in withVotes {
if votes == true {
positive += 1
} else {
negative += 1
}
}
return "\(forIssue) \(positive) yes, \(negative) no"
}

Creating a threadsafe Array, the easy way?

I've just read a post by Basem Emara about creating a threadsafe array Type in Swift. While I glanced through the code example, I asked myself if there isn't a way to achieve this with quite less code.
Suppose I create this class:
// MARK: Class Declaration
class ThreadsafeArray<Element> {
// Private Variables
private var __array: [Element] = []
private var __arrayQueue: DispatchQueue = DispatchQueue(
label: "ThreadsafeArray.__concurrentArrayQueue",
attributes: .concurrent
)
}
// MARK: Interface
extension ThreadSafeArray {
// ReadWrite Variables
var threadsafe: [Element] {
get {
return self.__arrayQueue.sync {
return self.__array
}
}
set(newArray) {
self.__arrayQueue.async(flags: .barrier) {
self.__array = newArray
}
}
}
}
If, from now on, I only accessed the actual array through .threadsafe, would this suffice to make the array threadsafe?
Also, could I implement it a struct instead of a class to get the mutating checks as well?
I am aware that the objects inside this array would not be threadsafe themselves through this but this is not the point, so let's assume I only put threadsafe stuff in there.
(Of course, to avoid the calls to .threadsafe, I would make the shiny new class conform to ExpressibleByArrayLiteral, Collection and RangeReplaceableCollection, so I can use it like a normal array.
Edit
Meanwhile, I've tried testing it in a playground and have come to believe that it doesn't suffice.
Playground code:
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// Testing //
// Thread-unsafe array
func unsafeArray() {
var array: [Int] = []
var iterations: Int = 1000
let start: TimeInterval = Date().timeIntervalSince1970
DispatchQueue.concurrentPerform(iterations: iterations) { index in
let last: Int = array.last ?? 0
array.append(last + 1)
DispatchQueue.global().sync {
iterations -= 1
// Final loop
guard iterations <= 0 else { return }
print(String(
format: "Unsafe loop took %.3f seconds, count: %d.",
Date().timeIntervalSince1970 - start, array.count
))
}
}
}
// Thread-safe array
func safeArray() {
let array: ThreadsafeArray<Int> = ThreadsafeArray<Int>()
var iterations: Int = 1000
let start: TimeInterval = Date().timeIntervalSince1970
DispatchQueue.concurrentPerform(iterations: iterations) { index in
let last: Int = array.threadsafe.last ?? 0
array.threadsafe.append(last + 1)
DispatchQueue.global().sync {
iterations -= 1
// Final loop
guard iterations <= 0 else { return }
print(String(
format: "Safe loop took %.3f seconds, count: %d.",
Date().timeIntervalSince1970 - start, array.threadsafe.count
))
}
}
}
unsafeArray()
safeArray()
Output:
Most of the time:
experiments(31117,0x7000038d0000) malloc: *** error for object 0x11f663d28: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Sometimes:
IndexError: Index out of range
Unfortunately also:
Unsafe loop took 1.916 seconds, count: 994.
Safe loop took 11.258 seconds, count: 515.
Doesn't seem to suffice (also, it's incredibly unperformant).
The synchronization mechanism in your question, with concurrent queue and judicious use of barrier is known as the “reader-writer” pattern. In short, it offers concurrent synchronous reads and non-concurrent asynchronous writes. This is a fine synchronization mechanism. It is not the problem here.
But there are a few problems:
In the attempt to pare back the implementation, this class has become very inefficient. Consider:
class ThreadSafeArray<Element> {
private var array: [Element]
private let queue = DispatchQueue(label: "ThreadsafeArray.reader-writer", attributes: .concurrent)
init(_ array: [Element] = []) {
self.array = array
}
}
extension ThreadSafeArray {
var threadsafe: [Element] {
get { queue.sync { array } }
set { queue.async(flags: .barrier) { self.array = newValue } }
}
}
let numbers = ThreadSafeArray([1, 2, 3])
numbers.threadsafe[1] = 42 // !!!
What that numbers.threadsafe[1] = 42 line is really doing is as follows:
Fetching the whole array;
Changing the second item in a copy of the array; and
Replacing the whole array with a copy of the array that was just created.
That is obviously very inefficient.
The intuitive solution is to add an efficient subscript operator in the implementation:
extension ThreadSafeArray {
typealias Index = Int
subscript(index: Index) -> Element {
get { queue.sync { array[index] } }
set { queue.async(flags: .barrier) { self.array[index] = newValue} }
}
}
Then you can do:
numbers[1] = 42
That will perform a synchronized update of the existing array “in place”, without needing to copy the array at all. In short, it is an efficient, thread-safe mechanism.
What will end up happening, as one adds more and more basic “array” functionality (e.g., especially mutable methods such as the removing of items, adding items, etc.), you end up with an implementation not dissimilar to the original implementation you found online. This is why that article you referenced implemented all of those methods: It exposes array-like functionality, but offering an efficient and (seemingly) thread-safe interface.
While the above addresses the data races, there is a deep problem in that code sample you found online, as illuminated by your thread-safety test.
To illustrate this, let’s first assume we flesh out our ThreadSafeArray to have last, append() and make it print-able:
class ThreadSafeArray<Element> {
private var array: [Element]
private let queue = DispatchQueue(label: "ThreadsafeArray.reader-writer", attributes: .concurrent)
init(_ array: [Element] = []) {
self.array = array
}
}
extension ThreadSafeArray {
typealias Index = Int
subscript(index: Index) -> Element {
get { queue.sync { array[index] } }
set { queue.async(flags: .barrier) { self.array[index] = newValue} }
}
var last: Element? {
queue.sync { array.last }
}
func append(_ newElement: Element) {
queue.async(flags: .barrier) {
self.array.append(newElement)
}
}
}
extension ThreadSafeArray: CustomStringConvertible {
var description: String {
queue.sync { array.description }
}
}
That implementation (a simplified version of the rendition found on that web site) looks OK, as it solves the data race and avoids unnecessary copying of the array. But it has its own problems. Consider this rendition of your thread-safety test:
let numbers = ThreadSafeArray([0])
DispatchQueue.concurrentPerform(iterations: 1_000) { <#Int#> in
let lastValue = numbers.last! + 1
numbers.append(lastValue)
}
print(numbers) // !!!
The strict data race is solved, but the result will not be [0, 1, 2, ..., 1000]. The problem are the lines:
let lastValue = numbers.last! + 1
numbers.append(lastValue)
That does a synchronized retrieval of last followed by a separate synchronized append. The problem is that another thread might slip in between these two synchronized calls and fetch the same last value! You need to wrap the whole “fetch last value, increment it, and append this new value” in a single, synchronized task.
To solve this, we would often give the thread-safe object a method that would provide a way to perform multiple statements as a single, synchronized, task. E.g.:
extension ThreadSafeArray {
func synchronized(block: #escaping (inout [Element]) -> Void) {
queue.async(flags: .barrier) { [self] in
block(&array)
}
}
}
Then you can do:
let numbers = ThreadSafeArray([0])
DispatchQueue.concurrentPerform(iterations: 1_000) { <#Int#> in
numbers.synchronized { array in
let lastValue = array.last! + 1
array.append(lastValue)
}
}
print(numbers) // OK
So let’s return to your intuition that the author’s class can be simplified. You are right, that it can and should be simplified. But my rationale is slightly different than yours.
The complexity of the implementation is not my concern. It actually is an interesting pedagogical exercise to understand barriers and the broader reader-writer pattern.
My concern is that (to my point 3, above), is that the author’s implementation lulls an application developer in a false sense of security provided by the low-level thread-safety. As your tests demonstrate, a higher-level level of synchronization is almost always needed.
In short, I would stick to a very basic implementation, one that exposes the appropriate high-level, thread-safe interface, not a method-by-method and property-by-property interface to the underlying array, which almost always will be insufficient. In fact, this desire for a high-level, thread-safe interface is a motivating idea behind a more modern thread-safety mechanism, namely actors in Swift concurrency.
I suspect this line is your issue:
DispatchQueue.global().sync { ...
If you specify one serial queue you want to use here you should get the result you want.
Something like:
let array = SynchronizedArray<Int>()
var iterations = 1000
let queue = DispatchQueue(label: "queue")
DispatchQueue.concurrentPerform(iterations: 1000) { index in
array.append(array.last ?? 0)
queue.sync {
iterations -= 1
if iterations == 0 {
print(array.count)
}
}
}
Another method of locking objects is:
func lock(obj: AnyObject, work:() -> ()) {
objc_sync_enter(obj)
work()
objc_sync_exit(obj)
}
Could your class use this to lock its standard array when needed?

Adding objects to a static array in swift

Is it possible to add subclassed objects to a static array inside the parent class? I would like to run a function on all instances created. Another SO question describes being able to loop over an array to call a function on each instance and I think this is the end result I would like as well but my problem is even getting the instances into a static array that keeps track of all instances.
Of course my code is more modular but here is a simplified view of the code and hierarchy:
class Jungle {
static var jungle: [Animals] = []
}
class Tigers: Animals {
static var tigerPopulation: Int = 0
override init(name:String){
super.init(name: name)
Tigers.tigerPopulation += 1
}
deinit {
Tigers.tigerPopulation -= 1
}
}
class Monkeys: Animals {
static var monkeysPopulation: Int = 0
override init(name: String){
super.init(name: name)
Monkeys.monkeysPopulation += 1
}
deinit {
Monkeys.monkeysPopulation -= 1
}
}
class Snakes: Animals {
static var snakePopulation: Int = 0
override init(name: String){
super.init(name: name)
Snakes.snakePopulation += 1
}
deinit {
Snakes.snakePopulation -= 1
}
}
I get the feeling that I should have created the Jungle class first so they all would subclass from Jungle I guess but I'm still stumped on how I would get the instances into an array.
Instead of trying to trigger some behavior to increase the monkey count, tiger count etc from within an initializer, it will be easer to just add everything to an animals array, and then use the animals array to figure out how many Tigers/Snakes/Monkeys there are.
Basing the count off that single array is going to be easier in the long term than having a bunch of different variables, and making sure they're in sync (e.g. by decreasing during deinit).
Here's a minor reshuffle of your jungle, animal, and animal subclasses:
class Jungle {
var animals: [Animal] = []
func addAnimal(animal: Animal) {
animals.append(animal)
}
// this is a computed property, everytime you access it
// via jungle.tigerPopulation, the code inside this statement
// executes, and you get the value that is returned
var tigerPopulation: Int {
// tigers is a new array containing only tigers
let tigers = animals.filter{ animal in
return animal is Tiger
}
// return the number of items in the tigers array
return tigers.count
}
}
class Animal {
let name: String
init(name: String) {
self.name = name
}
func sayHello() {
print("I'm an animal")
}
}
class Tiger: Animal {
// tiger specific stuff
override func sayHello() {
print("I'm a tiger called", name)
}
}
class Monkey: Animal {
// monkey specific stuff
}
class Snake: Animal {
// snake specific stuff
override func sayHello() {
print("I'm a snake called", name)
}
}
Something to point out is that because each of the animal subclasses are currently doing the same thing (assigning the name) you don't need to override the initializer in the subclasses - that's the value of class inheritance!
You can override the subclasses sometimes in the future if an animal has to do something that's different from other animals.
The most interesting thing that's happening here is the computed property that calculates the tiger population by filtering the tigers in the animals array, and returning the count, this way you're not manually +/- 1 the population count, but just calculating it whenever you ask for it.
Then you'd use your jungle object something like this:
let jungle = Jungle()
// jungle.animals -> []
// jungle.animals.count -> 0
// jungle.tigerPopulation -> 0
jungle.addAnimal(animal: Tiger(name: "Tony"))
// jungle.animals -> [Tiger()]
// jungle.animals.count -> 1
// jungle.tigerPopulation -> 1
jungle.addAnimal(animal: Snake(name: "Susan"))
// jungle.animals -> [Tiger(), Snake()]
// jungle.animals.count -> 2
// jungle.tigerPopulation -> 1
for animal in jungle.animals {
animal.sayHello()
}
// prints ->
// I'm a tiger called Tony
// I'm a snake called Susan

How to loop through array to find 4 identical values that are consecutive?

I have the following swift array:
var winSuitArray = [cardSuit1, cardSuit2, cardSuit3, cardSuit4, cardSuit5, cardSuit6, cardSuit7]
cardSuit1, cardSuit2 and so on, are variables that will equal strings like "clubs" or "hearts". What I want to do is loop through this array, and if the loop finds 4 identical objects whose indexes are consecutive, set the winSuitStatus bool to true.
For example, if the array looks like this:
["hearts", "clubs", "clubs", "clubs", "clubs", "diamonds", "spades"]
I want to loop through it like so:
for card in winSuitArray {
//find 4 identical and consecutive objects
// if the above requirements are met, let winSuitStatus = true
}
Is this possible to do?
To tell the truth, I'd probably do something similar to #KnightOfDragon's answer. There's nothing wrong with that approach. But this problem opens up some opportunities to build some much more reusable code at little cost, so it seems worth a little trouble to do that.
The basic problem is that you want to create a sliding window of a given size over the list, and then you want to know if any of the windows contain only a single value. So the first issue to to create these windows. We can do that very generally for all collections, and we can do it lazily so we don't have to compute all the windows (we might find our answer at the start of the list).
extension Collection {
func slidingWindow(length: Int) -> AnyRandomAccessCollection<SubSequence> {
guard length <= count else { return AnyRandomAccessCollection([]) }
let windows = sequence(first: (startIndex, index(startIndex, offsetBy: length)),
next: { (start, end) in
let nextStart = self.index(after: start)
let nextEnd = self.index(after: end)
guard nextEnd <= self.endIndex else { return nil }
return (nextStart, nextEnd)
})
return AnyRandomAccessCollection(
windows.lazy.map{ (start, end) in self[start..<end] }
)
}
}
The use of AnyRandomAccessCollection here is to just hide the lazy implementation detail. Otherwise we'd have to return a LazyMapSequence<UnfoldSequence<(Index, Index), ((Index, Index)?, Bool)>, SubSequence>, which would be kind of crazy.
Now are next question is whether all the elements in a window are equal. We can do that for any kind of Equatable sequence:
extension Sequence where Iterator.Element: Equatable {
func allEqual() -> Bool {
var g = makeIterator()
guard let f = g.next() else { return true }
return !contains { $0 != f }
}
}
And with those two pieces, we can just ask our question. In the windows of length 4, are there any runs that area all equal?
let didWin = suits.slidingWindow(length: 4).contains{ $0.allEqual() }
Or we could go a little different way, and create a SlidingWindowSequence that we could iterate over. The logic here is basically the same. This just wraps up the windowing into a specific type rather than a AnyRandomAccessCollection. This may be overkill for this problem, but it demonstrates another powerful pattern.
public struct SlidingWindowSequence<Base: Collection>: Sequence, IteratorProtocol {
let base: Base
let windowSize: Base.IndexDistance
private var windowStart: Base.Index
public init(_ base: Base, windowSize: Base.IndexDistance) {
self.base = base
self.windowSize = windowSize
self.windowStart = base.startIndex
}
public mutating func next() -> Base.SubSequence? {
if base.distance(from: windowStart, to: base.endIndex) < windowSize {
return nil
}
let window = base[windowStart..<base.index(windowStart, offsetBy: windowSize)]
windowStart = base.index(after: windowStart)
return window
}
}
let didWin = SlidingWindowSequence(suits, windowSize: 4).contains{ $0.allEqual() }
var suit = ""
var count = 1
for card in winSuitArray {
if(suit == card)
{
count++
}
else
{
count = 1
suit = card
}
if(count == 4)
{
//find 4 identical and consecutive objects
// if the above requirements are met, let winSuitStatus = true
}
}
You can use a counter variable to do this, initialized to 1.
for each value in array:
if value equals previous value
increment counter
else
counter = 1
if counter >= 4
set winCounter to true

Resources