how to change the subscript in an array of arrays - ios

I want to change subscript coordinates in an Array of Arrays [[Int]] in order to be able to address each value in a way similar to a spreadsheet, so assigning values, formulas across cells, etc.
Currently to address each value I need to subscript like table[2][1] = 12 or similar. I would like to subscript like a spreadsheet table[a][2] = 12, so that I can adapt long and complicated formulas in bigger speadsheets to similar tables using the same system used in spreadsheets.
The question is: how to change the subscript system in an efficient way? I send a simplified example to ilustrate the point
class ViewController: UIViewController {
var table = [[0, 1, 2, 3],
[1, 32, 44, 25],
[2, 12, 66, 43],
[3, 3, 4, 5]]
override func viewDidLoad() {
super.viewDidLoad()
print(table[2][1]) // 12
table[1][1] = 100
table[3][3] = table[1][1] * table[3][1] * 10
print(table[3][3])
printArray(table: table, j: 3)
}
// MARK: - Function
func printArray(table: [[Int]], j:Int) {
for i in 0...j {
print(table[i])
}
}
}

The closest solution I found is to use enums, like here:
enum Column:Int {
case a
case b
case c
// and so on
}
extension Array {
subscript(col:Column) -> Element {
get {
return self[col.rawValue]
}
set(newValue) {
self[col.rawValue] = newValue
}
}
}
var table = [[0, 1, 2, 3],
[1, 32, 44, 25],
[2, 12, 66, 43],
[3, 3, 4, 5]]
table[.a][2] = 12
let val = table[.a][2]
print (val)
Nevertheless, there are two little drawbacks:
you first have hard-code all the "column" letters you want to access to the Column enum.
then, something like table[5][.c] and table[.a][.b] will also be allowed - so you could access the rows via letter, not only the columns

Related

Combine arrays when they have similar element

I have 5 arrays inside an array,
let arrays = [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]
Now, I want to group them if they have atleast 1 similar element.
Expected output.
[[1,2,3,4,5,6,7], [21, 34, 89, 555]]
Since array 1, 2 and 3 has similar element, they will combine. And array 4 and 5 has similar element so they will be combined as well.
var finalSimilarArray = [[String]]()
let similararray = [[1,2,3,4,5,6,7], [21, 34, 89, 555]]
similarArray.forEach { array in
similarArray.enumerated().forEach { index, array2 in
if !Set(array2).intersection(Set(array)).isEmpty {
finalSimilarArray.append(contentsOf: [array] + [array2])
similarArray.remove(at: index)
}
}
}
I tried to looping and conditions with no luck, not even close. to the real deal.
Thank you, I hope I explained it clearly. :D
My 2 cent: Start with a function which merges a single array into a list of (mutually disjoint) arrays:
func mergeOne<T>(array: [T], into merged: [[T]]) -> [[T]]
where T: Hashable & Comparable
{
var set = Set(array)
var result = merged.compactMap { m -> [T]? in
if set.isDisjoint(with: m) {
return m
} else {
set.formUnion(m)
return nil
}
}
result.append(set.sorted()) // Or: result.append(Array(set))
return result
}
The array is converted to a set so that overlapping tests with another array and joining the elements can be done efficiently. This requires the elements to conform to the Hashable protocol. The Comparable conformance is only needed to sort the merged arrays – if that is not needed then this constraint can be removed.
The above function compares the given array with each element of the list, and joins them if there is an overlap. The important point is to use the new enlarged array (actually: set) for the subsequent comparisons. So arrays with no overlap with the new array are kept in the list, and the others are joined to a new array which is then appended to the list.
With that utility function we can merge a given list of arrays easily:
func merge<T>(arrays: [[T]]) -> [[T]]
where T: Hashable & Comparable
{
return arrays.reduce(into: [], { $0 = mergeOne(array: $1, into: $0) })
}
Starting with an empty list, the array elements are merged repeatedly.
Examples:
print(merge(arrays: [[1, 2, 3], [1, 2, 3, 4], [4, 5, 6, 7], [21, 34, 89], [555, 34]]))
// [[1, 2, 3, 4, 5, 6, 7], [21, 34, 89, 555]]
print(merge(arrays: [[1,2], [3, 4], [5, 6], [3, 5], [6, 2]]))
// [[1, 2, 3, 4, 5, 6]]
Compare each array element to every other array element to see if there is any overlap. If there is, then combine the arrays into a partial result. Next compare the partial result to each element in the results. If there is any overlap, then combine then. If not, then append the partial result to the array of results.
extension Collection where Element: Hashable {
func unique() -> [Element] {
return Array(Set(self))
}
}
extension Collection where Element: Equatable {
func containsAny(of other: Self) -> Bool {
for element in other {
if contains(element) {
return true
}
}
return false
}
}
func merge(arrays: [[Int]]) -> [[Int]] {
return arrays.reduce(into: Array<[Int]>(), { results, slice1 in
var partial = slice1
for slice2 in arrays {
if slice2.containsAny(of: partial) {
partial.append(contentsOf: slice2)
}
}
if let index = results.firstIndex(where: { $0.containsAny(of: partial) }) {
results[index] = (results[index] + partial).unique().sorted()
} else {
results.append(partial.unique().sorted())
}
})
}
print(merge(arrays: [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]))
print(merge(arrays: [[1,2], [3, 4], [1, 3]]))
Results are:
[[1, 2, 3, 4, 5, 6, 7], [21, 34, 89, 555]]
[[1, 2, 3, 4]]
You can use a Set which guarantees unique values, when using .union which merges the values of two sets into one.
let givenArray: [Set<Int>] = [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]
let expectedArray: [Set<Int>] = [[1,2,3,4,5,6,7], [21, 34, 89, 555]]
let groupedArray: [Set<Int>] = givenArray.reduce([]) { (result, numbers) -> [Set<Int>] in
var mutableResult = result
if let indexOfNumbersToMergeWith = mutableResult.firstIndex(where: { (set) -> Bool in
!set.isDisjoint(with: numbers) // Returns: `true` if the set has no elements in common with `other`
}) {
mutableResult[indexOfNumbersToMergeWith] = mutableResult[indexOfNumbersToMergeWith].union(numbers)
} else {
mutableResult.append(numbers)
}
return mutableResult
}
print(groupedArray == expectedArray) // prints: true
EDIT:
I've updated the answer to support the following given array [[1,2], [3,4], [1,3]]
let givenArray: [Set<Int>] = [[1,2], [3,4], [1,3]]
let expectedArray: [Set<Int>] = [[1,2,3,4]]
let groupedArray: [Set<Int>] = givenArray.reduce([]) { (result, numbers) -> [Set<Int>] in
var mutableResult = result
let indexesOfNumbersToMergeWith: [Int] = mutableResult.enumerated().reduce([], { (result, arguments) -> [Int] in
var mutableResult = result
let (index, numbersToCompare) = arguments
// Returns: `true` if the set has no elements in common with `other`
if !numbersToCompare.isDisjoint(with: numbers) {
mutableResult.append(index)
}
return mutableResult
})
if !indexesOfNumbersToMergeWith.isEmpty {
// Note that I've sorted the indexes in descending order. This is because everytime you remove an element, the indexes of the elements beyond is reduce by one.
let numbersToMergeWith = indexesOfNumbersToMergeWith.sorted(by: { $1 < $0 }).map { (index) -> Set<Int> in
return mutableResult.remove(at: index) // removes and returns number set
}
mutableResult.append(numbersToMergeWith.reduce(into: numbers, { $0 = $0.union($1) }))
} else {
mutableResult.append(numbers)
}
return mutableResult
}
print(groupedArray == expectedArray) // prints: true

How to remove one duplicate value in an array?

I have two arrays for which I am comparing [Int]
let filter = strongAgainstArray.filter{weakAgainstArray.contains($0)}
This returns an array of common values in the 2 arrays. I then want to go through and remove those values from each array, which I'm doing like so
for item in filter {
for var i = 0; i < strongAgainstArray.count; i += 1 {
if item == strongAgainstArray[i] {
strongAgainstArray.removeAtIndex(i)
print("Removing a Strong against index \(item)")
}
}
for var i = 0; i < weakAgainstArray.count; i += 1 {
if item == weakAgainstArray[i] {
weakAgainstArray.removeAtIndex(i)
print("Removing a Weak against index \(item)")
}
}
}
This works fine, but let's say one of my arrays contains two entries for 12 as an example. How do I only remove one of them? As it stands, all entries of 12 are being removed entirely.
EDIT
I'm now comparing my two arrays using
let commonValues = Array(Set(strongAgainstArray).intersect(weakAgainstArray))
and then those commonValues from each array with
cleanStrongAgainstArray = Array(Set(strongAgainstArray).subtract(Set(commonValues)).sort())
cleanWeakAgainstArray = Array(Set(weakAgainstArray).subtract(Set(commonValues)).sort())
This is a much better overall solution, but I'm still eventually running into the same issue, albeit slightly different than before.
In the playground, for example...
let array = [7,7,9]
let test = Array(Set(array))
test comes back containing [7, 9], and I need to keep that extra 7. How do I do that?
If the order of the arrays aren't important then you can easily achieve the whole solution using Sets:
let dirtyArray = [1,4,6,1,56,4,4,66,23,3,3,12]
let dirtyArray1 = [3,1,6,99,54]
let cleanArray = Array(Set(dirtyArray).union(Set(dirtyArray1)))
print (cleanArray)
[12, 54, 23, 4, 6, 66, 99, 56, 1, 3]
If order is important, use NSOrderedSet:
let strongAgainstArray = [1, 2, 3, 4]
let weakAgainstArray = [3, 4, 5, 6]
let result = NSOrderedSet(array: (strongAgainstArray + weakAgainstArray)).array

Sum progression in an array Swift

I have an basic Int array
Array = [8, 9, 8]
How do i sum all of its values progressively so that the end result would look like this
EndResult = [8, 17, 25]
Tried using for and while loops, but to no avail.
NB: Basic array[0] + array[1] advices will not work. I'm looking for something automatic like a loop solution.
Looking forward to your advices.
Thanks!
May be this:
var arr = [8, 9, 8]
for i in 1..<arr.count {
arr[i] += arr[i-1]
}
print(arr)
Probably there are better ways than this one, but it works
var array = [8, 9, 8]
var result = [Int]()
for i in 0..<array.count{
var temp = 0;
for j in 0...i{
temp+=array[j]
}
result.append(temp)
}
print(result) //[8, 17, 25]
You could use a reduce function in Swift to accomplish this. Note that you can't really do it with map, because you would need to know what the previous call to the map function returned, keep state in a variable outside the map function (which seems dirty), or loop over your array for every map function call.
let array = [8, 9, 8]
let results = array.reduce((0, []), combine: { (reduction: (lastValue: Int, values: Array<Int>), value: Int) in
let newValue = reduction.lastValue + value
return (newValue, reduction.values + [newValue])
}).1

Append array of arrays Swift

I have an array of arrays called Lines
Numbers = [[1,2,3],[4,5,6],[7,8,9]]
and var numberInt = 10
how do i append this numberInt inside the last array of the multidimensional array? so it would look like
Numbers = [[1,2,3],[4,5,6],[7,8,9,10]]
Thank you.
Use Numbers[Numbers.count - 1] to get last one-dimensional array and append method to add item:
Numbers[Numbers.count - 1].append(numberInt)
print(Numbers) // prints "[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]"
I'd also recommend you to take a look at this answer: https://stackoverflow.com/a/25128237/1796907

how to iterate over dictionary in swift

I'm doing the apple playground tutorial and came across this section:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestKind = String()
for (kind, numbers) in interestingNumbers {
println(kind)
for number in numbers {
if number > largest {
largest = number
largestKind = kind
}
}
}
largest
largestKind
The problem is that it is not finding and printing the largest kind. Am I doing something wrong?
The code you posted works perfectly for me. I assume that you are viewing the output in playgrounds. However, playgrounds isn't always perfect. Try print(largestKind) or try adding some random code after that or open a new playground.

Resources