NSRange array of odd or even count - ios

I have an array of 977 of data, I've been trying to split it to 20 section in UITableView but i cannot get it correct, i am trying to do it dynamically. like if i got an array of 312 or 32 or 545 the equation should divide it, and add the last odd elements in array, I'm placing the new data in array of arrays.
So here is what I'm doing :
var dataof977 = Mydata()
var mutA = NSMutableArray()
for (var i = 0; i < 19; i++)
{
var halfArray : NSArray!
var theRange = NSRange()
theRange.location = i*19;
theRange.length = dataof977.afa.count / 19
halfArray = dataof977.afa.subarrayWithRange(theRange)
mutA.addObject(halfArray)
}
Note : dataof977 is reference of class and afa is a String array.
What am i missing here ?s

Three things:
You need to start each location where the previous left off. To do this, introduce a location variable to keep track of where you are in the original array.
Some of your sections will need more items since your count might not be a multiple of 20. I think your best best is to give the first n sections an extra item to make up for the leftovers.
Your loop needs to iterate 20 times, not 19. I have changed it to use for in which is better Swift style.
var mutA = NSMutableArray()
let sublength = dataof977.afa.count / 20
let leftovers = dataof977.afa.count % 20
// location of each new subarray
var location = 0
for i in 0..<20
{
var length = sublength
// The first sections will have 1 more each
if i < leftovers {
length++
}
let theRange = NSMakeRange(location, length)
let halfArray = dataof977.afa.subarrayWithRange(theRange)
mutA.addObject(halfArray)
// move location up past the items we just added
location += length
}

If you have the possibility to work with Swift arrays instead of NSArray, you can use stride to iterate by steps and divide an array in equal parts with the remainder elements in the last array:
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
extension Array {
func splitBy(subSize: Int) -> [[Element]] {
return 0.stride(to: self.count, by: subSize).map { startIndex in
let endIndex = startIndex.advancedBy(subSize, limit: self.count)
return Array(self[startIndex ..< endIndex])
}
}
}
let chunks = arr.splitBy(5)
print(chunks) // [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
Note: this is for Swift 2.

You are only doing 19 iterations, not 20.
for var i = 0; i < 20; i++
You are taking one 19th of the data count, not one 20th. You are repeating the length calculation 19 times in the loop, which is redundant.
It would be easier to dispense with NSRange. Just iterate though all array elements, keep a counter up to the pre-calculate subarray size, and once it reaches the critical number, reset it to zero and start a new subarray to add to your result array.
This will presumably perform better if you use Swift arrays, but this is not strictly necessary.

Related

Summing columns in a 2D array in Swift

I have a simple 2D array of integers named "round", with 5 elements on the horizontal and a variable number of "rows".
I want to calculate the total for each "column". Here's my code:
var totals = [Int] ()
for column in 0...4 {
for row in 0...round.count-1 {
totals[column] = round[row][column] + totals[column] }
}
I get a Playground "Fatal error: Index out of range"
In trying various options, it appears to be the "totals[column] = " assignment that throws the error, but I cannot determine why?
The problem is totals[column] since totals is an empty array it has no element at the index column.
It's similar to the following attempt:
var arr = [Int]()
arr[0] = 1 // <- fails, because there is no element at index 0
To fix this issue, you can initialize totals with zeros for the number of elements you want to store in the array:
var totals = Array(repeating: 0, count: round[0].count)
Or even simpler (but less dynamic):
var totals = [0, 0, 0, 0, 0]

Pairing duplicate elements in an array

I am trying to pair the duplicate elements of an array and count the pairs.
When given array is : [10, 20, 20, 10, 10, 30, 50, 10, 20], I'm expecting numberOfPairs to be 3. Because there are 2 pairs of 10s and 1 pair of 20.
My "if condition" is checking if current element's index is first index or not. If it is not the last index, it means that there is a duplicate of the current element. So I'am adding 1 to numberOfPairs.
For input [10, 20, 20, 10, 10, 30, 50, 10, 20], my numberOfPairs is 2 but it should be 3.
For input [1 1 3 1 2 1 3 3 3 3], myNumberOfPairs is not printing at all? But instead it should be 4.
My What am I missing here?
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
for i in 0..<array.count {
var element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
print("indexOfLastElement is \(indexOfLastElement)")
print("indexOfFirstElement is \(indexOfFirstElement)")
if indexOfFirstElement != indexOfLastElement {
numberOfPairs += 1
array.remove(at: indexOfFirstElement!)
array.remove(at: indexOfLastElement!)
continue
}
return numberOfPairs
}
return numberOfPairs
}
You're mutating your array by calling remove(at:) at the same time as you're accessing it which is why you're having these weird side effects.
I assume you're trying to solve a Leetcode task (or something similar), so I won't provide a solution upfront. My suggestion for you is to think of an algorithm that doesn't involve changing the contents of the List while you're reading these contents of that same List.
I'm agree with #MartinR that in such cases you should place breakpoints and go throught your code line by line, glad you've found your mistake by yourself.
But also in terms of performance, lastIndex and firstIndex are very heavy operations, because they may go thought all items and find nothing, which makes Big O notation of your algorithm around O(log n). In such cases dictionary is widely used(if you're not much limited with the space).
You can use value as a key and count as a value for a dictionary and count all items, then just sum like this:
func sockMerchant(ar: [Int]) -> Int {
ar.reduce(into: [Int:Int]()) { map, value in
map[value, default: 0] += 1
}.reduce(0) { sum, count in
sum + count.value / 2
}
}
So, I've solved the problem as below, thanks to #Vym and #Martin R.
func sockMerchant(n: Int, ar: [Int]) -> Int {
// Write your code here
var array = ar
var numberOfPairs = 0
var newArray = [Int]()
var done = false
for i in 0..<array.count {
let element = array[i]
let indexOfLastElement = array.lastIndex(of: element)
let indexOfFirstElement = array.firstIndex(of: element)
if indexOfFirstElement != indexOfLastElement {
newArray.append(element)
numberOfPairs = newArray.count/2
done = true
}
}
if done == true {
return numberOfPairs
}
return numberOfPairs
}

how to store values in a 1D array into a 2D array in Swift 4

Hi I would like to store values of a 1D array into a 2D array.
My 1D array has 50 elements and I want to store it in a 5x10 array, but whenever I do that, it always gives me a "Index out of range" error
Any help would be appreciated thanks!
var info2d = [[String]]()
var dataArray = outputdata.components(separatedBy: ";")
for j in 0...10 {
for i in 0...5 {
info2d[i][j] = dataArray[(j)*5+i]
print(info2d[i][j])
}
}
Lots of error in your code.
info2d must be initialised with default values before using it by index
// initialising 2d array with empty string value
var info2d = [[String]](repeating: [String](repeating: "", count: 10), count: 5)
Secondly for loop with ... includes the last value too, use ..<
for j in 0..<10 {
//...
}
Thirdly (j)*5+i is incorrect too.
Better Read how to use arrays, collections and for loop in swift.
https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html
https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html
I would make use of ArraySlice for this.
var arr2D = [[String]]()
for i in 0..<5 {
let start = i * 10
let end = start + 10
let slice = dataArray[start..<end] //Create an ArraySlice
arr2D.append(Array(slice)) //Create new Array from ArraySlice
}

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

Swift Array Running Total to x item

Swift has a great function .reduce that provides a running total for an entire array:
let array = [1, 2, 3, 4, 5]
let addResult = array.reduce(0) { $0 + $1 }
// addResult is 15
let multiplyResult = array.reduce(1) { $0 * $1 }
// multiplyResult is 120
But is there a simple way to use this for a running total only up to a specific element? For example for an array of [1, 2, 3, 4, 5] is there a way to use the .reduce function to return a running total for only up to the 3rd item (1+2+3=6)?
(I know I can always do a loop, just trying to learn all the possibilities with swift)
Besides using the reduce method of arrays, as in #MartinR's answer, it's also possible to use the global reduce function along with enumerate
The enumerate function returns a sequence of tuples, whose first element (named index) is the element index and the second (named element) is the element value.
So you can achieve the same result with the following code:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index < 3 ? $1.element : 0)
}
The advantage of this method is that you can apply more complex rules to exclude elements - for example, to sum all elements having even index:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index % 2 == 0 ? $1.element : 0)
}
The closure called by reduce() has no information about the
current index.
Theoretically it is possible to create a closure that
uses a captured variable to "remember" how often it has been called:
func addFirst(var count : Int) -> (Int, Int) -> Int {
return {
count-- > 0 ? $0 + $1 : $0
}
}
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array.reduce(0, addFirst(3) )
But this would still run over the whole array and not just the
first 3 elements. There is no way to "stop" the process.
A much simpler way is to operate on an array slice:
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array[0 ..< 3].reduce(0) { $0 + $1 }
A slice is an Array-like type that represents a sub-sequence of any
Array, ContiguousArray, or other Slice.

Resources