Need to create an array of sets swift - arrays

I'm brand new to Swift, and it's been awhile since I've done any programming at all so please forgive me. I need some help on how to create an empty array that would contain a set of numbers.
What I'm trying to do is read two sets of numbers in from two different data files, and place them into two different data structures - in this case - arrays. I then want to loop through the array and determine if one group of numbers is a subset of the other. I have created the following code in the swift playground, and tested it and I know it can be done using pre-defined values in the code.
However, I can't seem to find anywhere online how to create an array of sets. I find all sorts of links that say when to use an array rather than a set and vice versa. When I try to declare an empty Array of type Set it gives me an error. I would appreciate anyone pointing me in the right direction. Here is the code that I typed into the playground that works.
var a: Set = [1,2]
var b: Set = [1,3]
var c: Set = [1,4]
var aa: Set = [1,4,23,29,50]
var bb: Set = [1,3,45,47,65]
var cc: Set = [7,9,24,45,55]
let combiArray = [a, b, c]
let resultsArray = [aa, bb, cc]
for i in 0...2 {
print (resultsArray[i],
combiArray[i],
combiArray[i].isSubset(of: resultsArray[i]))
}

Set is a generic type. When you say var a: Set = [1, 2], the compiler infers the necessary generic type parameter for you, making it equivalent to var a: Set<Int> = [1, 2]
To make an empty Array of Sets, you have to explicitly state what kind of Set you want, because the compiler can't infer it from the Set's contents. You're looking to make an empty Array<Set<Int>>, a.k.a. [Set<Int>].
Either:
let arrayOfSets: [Set<Int>] = []
or:
let arrayOfSets = [Set<Int>]() // preferred
Here's that reflected in your example:
let combiArray: [Set<Int>] = [ // TODO: What the heck is a "combi"?
[1, 2],
[1, 3],
[1, 4],
]
let results: [Set<Int>] = [
[1, 4, 23, 29, 50],
[1, 3, 45, 47, 65],
[7, 9, 24, 45, 55],
]
for (combi, result) in zip(combiArray, results) {
print("\(combi) \(combi.isSubset(of: result) ? "is" : "is not") a subset of \(result).")
}

Related

How do I append values fo an observed array in RxSwift

Trying to grasp RxSwift and get stuck on a few things.
var observedData = BehaviorSubject.from([2, 3, 4, 5, 6])
.map({$0*3}).subscribe(onNext: {
print("HELLO", $0)
})
How do I append an extra value to the array, so that the subscription is triggered again?
I tried observedData.onNext and observedData.accept but they don't work.
I also would like to know the difference between
var observedData = BehaviorSubject.from([2, 3, 4, 5, 6])
and
var observedData2 = BehaviorSubject<[Int]>(value: [2, 3, 4, 5, 6])
I first assumed it was different ways of writing the same thing, but I can't use .map on observedData2
Along with the answer #EtienneJézéquel gave...
The public static func ObservableType.from(_:) function returns an Observable whereas the BehaviorSubject.init(value:) creates a BehaviorSubject which must then be converted to an Observable before you can map(_:) it.
Also, it might help to understand better when you realize you don't append to the array that is contained by the BehaviorSubject, instead you emit a new array using it. That's why Etienne's code first copies the current array out of the subject using value() throws and appends to the copy and then pushes the new array into the subject using onNext(_:).
Lastly, don't make subjects vars they should always be lets because you don't want to reseat them after setting up chains to them.
something like that should work :
let subject = BehaviorSubject<[Int]>(value: [2, 3, 4, 5, 6])
subject.asObservable().map({$0.map({$0*3})}).subscribe(onNext: { print("HELLO", $0) }).disposed(by: disposeBag)
if var value = try? subject.value() {
value.append(1)
subject.on(.next(value))
}

How to get a pointer (instead of a copy) to an array in Swift?

In Swift, assigning an array to a new variable actually makes of copy. For example (as in Apple doc for Array):
var numbers = [1, 2, 3, 4, 5]
var numbersCopy = numbers
numbers[0] = 100
print(numbers)
// Prints "[100, 2, 3, 4, 5]"
print(numbersCopy)
// Prints "[1, 2, 3, 4, 5]"
How do I actually get a pointer to the same array, so modifying the elements is reflected in the same array? (The reason for this is I access in static instances of another class, e.g. "SomethingManager.sharedInstance.arrayList[aKey]" and I'll like to shorten it to an assigned pointer variable.)
(I'm interested to know how to do this in Swift 4 and 5. I don't see any existing question for Swift language.)
EDIT:
I'm providing my rationale for the need to have a pointer instead of a copy.
Say, I have the following code:
var childrenTasks = [Int64: [TaskRef]]()
defined in a class, which is accessed:
MyClass.singleton.parentTask[parentTaskID].childrenTask[taskRefID]
As you can see that the code to access childrenTask is very long. I'd like to have a pointer, just an illustration :-
var aPointerToChildrenTasks = MyClass.singleton.parentTask[parentTaskID].childrenTask[taskRefID] // I want a pointer, not a copy!
aPointerToChildrenTask.remove(at: anIndex) // if it is a pointer, I can manipulate the same set of values of the array
It will help make my code easier to read. I need a pointer to manipulate the same set of values so I use a "var". If it is only read-only, I can use a "let", but still it has performance penalty if I get a copy.
How do I get a pointer in Swift? Is this possible? (I know that in Kotlin it is possible as it is pass-by reference.)
EDIT: I see some suggestion that this question is a duplicate. No, it is not. Those other questions/answers are specifically focused on inout parameters. For my case, I just want a pointer to work in the same function/method.
Not a ‘pure’ Swift solution, but using NSArray will give you the reference semantics you desire.
NSArray is toll-free bridgeable to Array, so you can use plain as instead of as!
var numbers = [1, 2, 3, 4, 5]
var numbersCopy = numbers as NSArray
numbers[0] = 100
print(numbers)
[100, 2, 3, 4, 5]
print(numbersCopy as Array)
[1, 2, 3, 4, 5]
If you are modifying the 'copy' you will need to use a NSMutableArray.
Edit:
oops 🤭
I think I was confused by the naming of your variable numbersCopy. I see now that you want the 'copy' to share the same value as the original. By capturing the variable numbers in a block, and executing that block later, you can get the current value of numbers, and you don't need to use NSArray at all.
var numbers = [1, 2, 3, 4, 5]
var numbersCopy = {numbers}
numbers[0] = 100
print(numbers)
[100, 2, 3, 4, 5]
print(numbersCopy())
[100, 2, 3, 4, 5]
If it's just about convenience, consider making a utility function like this:
func withChildrenTasks(of parentTaskID: Int64, taskRefID: TaskRef, body: (inout [TaskRef]) -> ()) {
body(&MyClass.singleton.parentTask[parentTaskID].childrenTasks[taskRefID])
}
withChildrenTasks(of: parentTaskID, taskRefID: taskRefID) { tasks in
// do stuff with tasks
}
You can't create an "inout var", but you can always make a callback that accepts an inout parameter, so this is an easy workaround. I expect that the Swift compiler would be pretty good about optimizing it away.
If it's because you actually want to share the array reference, you will either need to wrap it in a reference type (class SharedArray<T> { var array = [T]() } might be enough for that purpose), or you could use NSMutableArray from Foundation.
Use a computed property:
var numbers = [1, 2, 3, 4, 5]
var numbersCopy: [Int] {
get { numbers }
set { numbers = newValue }
}
numbers[0] = 100
print(numbers)
// Prints "[100, 2, 3, 4, 5]"
print(numbersCopy)
// Prints "[100, 2, 3, 4, 5]"
numbersCopy[1] = 200
print(numbers)
// Prints "[100, 200, 3, 4, 5]"
print(numbersCopy)
// Prints "[100, 200, 3, 4, 5]"

How to find a random index in array A which value does not appear in array B?

Let's say array A holds this:
[0, 1, 8, 3, 10, 6, 2]
And array B holds this:
[1, 2]
How can I generate a random index in array A which value does not appear in array B? Possible indexes in above example are:
0, 2, 3, 4, 5
But how to do this in Swift?
When you want to work with Array elements and their indices, enumerated() can be a good tool:
var a = [0, 1, 8, 3, 10, 6, 2]
var b = [1, 2]
var possibleIndices = a.enumerated()
.filter{!b.contains($0.element)}
.map{$0.offset}
print(possibleIndices)
//->[0, 2, 3, 4, 5]
(When b can be large, better make it a Set.)
And then:
(When we can assume b never holds all contents of a.)
var randomIndexToPossibleIndices = Int(arc4random_uniform(UInt32(possibleIndices.count)))
var randomIndex = possibleIndices[randomIndexToPossibleIndices]
If the assumption above cannot be satisfied, possibleIndices can be empty. So you'd better make randomIndex Optional:
var randomIndex: Int? = nil
if !possibleIndices.isEmpty {
var randomIndexToPossibleIndices = Int(arc4random_uniform(UInt32(possibleIndices.count)))
randomIndex = possibleIndices[randomIndexToPossibleIndices]
}
Thanks for Martin R.
First, you'd have to generate a diff between the 2 arrays ( unless they're both extremely large, in which case randomly trying recursively might result in better performance ).
Then all you have to do is find a random index you'd like to use and access said element:
#if os(Linux)
let j = Int(random() % ((count-1)))
#else
let j = Int(Int(arc4random()) % ((count-1)))
#endif
Will give you a proper index
If you then use this index and the element to find original element in your array you'll have your result.
If in case your elements are integers, and thus collisions can occur the thing I'd do would be recursively finding it to solve your problem. Remember that this can result in slow performance.
Look into the functional programming part of collections in swift here:
Swift Guide to map filter reduce
For instance you could use filter in the following way ( and I don't know if this is the best way ):
collection.filter {
var found = false;
for element in bCollection {
if element == $0 {
found = true;
}
}
return !found; // Might be better to turn true/false thing around in the above code to slightly improve performance.
}
How about working with sets?
let a = [0, 1, 8, 3, 10, 6, 2]
let b = [1, 2]
var setA = Set(a)
var setB = Set(b)
setA.subtract(setB)
var index: Int? = nil
if let first = setA.first {
index = a.index(of: first)
}
// if index == nil no such index exists

Splitting an Array into Sub-Arrays in Swift [duplicate]

This question already has answers here:
In Swift, an efficient function that separates an array into 2 arrays based on a predicate
(7 answers)
Closed 6 months ago.
Problem
Given an array of values how can I split it into sub-arrays made of elements that are equal?
Example
Given this array
let numbers = [1, 1, 1, 3, 3, 4]
I want this output
[[1,1,1], [3, 3], [4]]
What I am NOT looking for
A possible way of solving this would be creating some sort of index to indicate the occurrences of each element like this.
let indexes = [1:3, 3:2, 4:1]
And finally use the index to rebuild the output array.
let subsequences = indexes.sort { $0.0.0 < $0.1.0 }.reduce([Int]()) { (res, elm) -> [Int] in
return res + [Int](count: elm.1, repeatedValue: elm.0)
}
However with this solution I am losing the original values. Of course in this case it's not a big problem (an Int value is still and Inteven if recreated) but I would like to apply this solution to more complex data structures like this
struct Starship: Equatable {
let name: String
let warpSpeed: Int
}
func ==(left:Starship, right:Starship) -> Bool {
return left.warpSpeed == right.warpSpeed
}
Final considerations
The function I am looking for would be some kind of reverse of flatten(), infact
let subsequences: [[Int]] = [[1,1,1], [3, 3], [4]]
print(Array(subsequences.flatten())) // [1, 1, 1, 3, 3, 4]
I hope I made myself clear, let me know should you need further details.
// extract unique numbers using a set, then
// map sub-arrays of the original arrays with a filter on each distinct number
let numbers = [1, 1, 1, 3, 3, 4]
let numberGroups = Set(numbers).map{ value in return numbers.filter{$0==value} }
print(numberGroups)
[EDIT] changed to use Set Initializer as suggested by Hamish
[EDIT2] Swift 4 added an initializer to Dictionary that will do this more efficiently:
let numberGroups = Array(Dictionary(grouping:numbers){$0}.values)
For a list of objects to be grouped by one of their properties:
let objectGroups = Array(Dictionary(grouping:objects){$0.property}.values)
If you could use CocoaPods/Carthage/Swift Package Manager/etc. you could use packages like oisdk/SwiftSequence which provides the group() method:
numbers.lazy.group()
// should return a sequence that generates [1, 1, 1], [3, 3], [4].
or UsrNameu1/TraverSwift which provides groupBy:
groupBy(SequenceOf(numbers), ==)
If you don't want to add external dependencies, you could always write an algorithm like:
func group<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [[S.Generator.Element]] {
var result: [[S.Generator.Element]] = []
var current: [S.Generator.Element] = []
for element in seq {
if current.isEmpty || element == current[0] {
current.append(element)
} else {
result.append(current)
current = [element]
}
}
result.append(current)
return result
}
group(numbers)
// returns [[1, 1, 1], [3, 3], [4]].
Let's assume that you have an unsorted array of items. You will need to sort the initial array then you will have something like this:
[1, 1, 1, 3, 3, 4]
After that you will initialize two arrays: one for storing arrays and another one to use it as a current array.
Loop through the initial array and:
if the current value isn't different from the last one, push it to the current array
otherwise push the current array to the first one then empty the current array.
Hope it helps!
Worth mentioning, using Swift Algorithms this is now a one-liner:
import Algorithms
let numbers = [1, 1, 1, 3, 3, 4]
let chunks: [[Int]] = numbers.chunked(by: ==).map { .init($0) }
print(chunks) // [[1, 1, 1], [3, 3], [4]]

Populate Array with a set of Strings from a for-in loop for Swift

I am kinda stumped on figuring this out. I want to populate an array with the string values that comes from a for-in loop.
Here's an example.
let names = ["Anna", "Alex", "Brian", "Jack"]
for x in names {
println(x)
}
The current x value would generate 4 string values (Anna, Alex, Brian, Jack).
However I need some advice in going about getting these four values back into an array. Thank you in advance.
Whatever is on the right side of a for - in expression must be a SequenceType. Array, as it happens, can be initialised with any SequenceType. So if you're just doing something like this:
var newArray: [String] = []
for value in exoticSequence {
newArray.append(value)
}
The same thing can be accomplished (faster), by doing this:
let newArray = Array(exoticSequence)
And it doesn't matter what type exoticSequence is: if the for-in loop worked, Array() will work.
However, if you're applying some kind of transformation to your exoticSequence, or you need some kind of side effect, .map() might be the way to go. .map() over any SequenceType can return an array. Again, this is faster, and more clear:
let exoticSequence = [1, 2, 3]
let newArray = exoticSequence.map {
value -> Int in
// You can put whatever would have been in your for-in loop here
print(value)
// In a way, the return statement will replace the append function
let whatYouWouldHaveAppended = value * 2
return whatYouWouldHaveAppended
}
newArray // [2, 4, 6]
And it's equivalent to:
let exoticSequence = [1, 2, 3]
var newArray: [Int] = []
for value in exoticSequence {
print(value)
let whatYouWouldHaveAppended = value * 2
newArray.append(whatYouWouldHaveAppended)
}
newArray // [2, 4, 6]

Resources