Hello I'm trying to do pagination for array of contacts, some how i the code crashed on paged 2
here is my code :
// Initialize
let limit : Int = 10
var page : Int = self.defaults.integer(forKey: "ConPage") == 0 ? 1 : self.defaults.integer(forKey: "ConPage")
var start : Int = page == 0 || page == 1 ? 0 : limit * ( page-1)
var increment : Int = 1
var data = (contacts)?[start...limit]
print("[CONTACTS SYNC][LoadUpContacts] Success \(success) , Data : \(data?.count) , start : \(start) , Limit : \(limit) , Page : \(page), Total : \(contacts?.count) ")
for contact in data!
{
print("\(increment) : \(contact.name) ")
increment = increment + 1
}
//go Next
self.defaults.set(page+1, forKey: "ConPage")
self.LoadUpContacts()
and here is the crash log :
fatal error: Can't form Range with upperBound < lowerBound
2017-07-10 11:32:01.758790+0300 muzeit[6085:2216770] fatal error: Can't form Range with upperBound < lowerBound
What is the best way to paginate an array in swift 3 ?
the problem was in array range , so i fixed it and i hope it solve someone else issue , below is the correct code for array pagination in Swift 3
let total : Int = (contacts?.count)!
let limit : Int = 20
let page : Int = self.defaults.integer(forKey: "ConPage") == 0 ? 1 : self.defaults.integer(forKey: "ConPage")
let start : Int = page == 0 || page == 1 ? 0 : (limit * page) - limit
var end : Int = start + limit
end = end >= total ? total : end
end = end - 1
let data = start >= total ? [] : (contacts)?[start...(end)]
To respond to your question in the comment. In fact, the subArrayWithRange is only available in Objective-c. So you can create a subarray from an index, with length by:
let arr = [1, 3, 4, 5, 10, 2, 100, 40, 1244, 23]
let startIndex = 3
let length = 4
let arr2 = arr[startIndex..<(startIndex + length)] //print [5, 10, 2, 100]
And to get the contents of a page from your items, you have to calculate the right startIndex and the endIndex of your subarray. Be attention that, the subcript returns a ArraySlice, not Array. You may to cast it to Array.
//page start from 0
func getPageItems(page: UInt, allItems: [Int], maxItemsPerPage: UInt) -> [Int] {
let startIndex = Int(page * maxItemsPerPage)
var length = max(0, allItems.count - startIndex)
length = min(Int(maxItemsPerPage), length)
guard length > 0 else { return [] }
return Array(allItems[startIndex..<(startIndex + length)])
}
the call:
let arr3 = getPageItems(page: 3, allItems: arr, maxItemsPerPage: 4)
will return [1244, 23]
Looking at the error it seems like there is an issue with the array range.
If you look at the line:
var data = (contacts)?[start...limit]
of your code, you can see that the limit is always equal to 10. And the range upper...lower represents the start and the end of the range. So you should properly calculate the lower bound also in the way you calculate the upper bound.
Related
I want to add the numbers together and print every 4 elements, however i cannot wrap my head around using the stride function, if i am using the wrong approach please explain a better method
var numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13]
func addNumbersByStride(){
var output = Stride...
//first output = 1+2+3+4 = 10
//second output = 5+6+7+8 = 26 and so on
print(output)
}
It seems you would like to use stride ...
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13]
let by = 4
let i = stride(from: arr.startIndex, to: arr.endIndex, by: by)
var j = i.makeIterator()
while let n = j.next() {
let e = min(n.advanced(by: by), arr.endIndex)
let sum = arr[n..<e].reduce(0, +)
print("summ of arr[\(n)..<\(e)]", sum)
}
prints
summ of arr[0..<4] 10
summ of arr[4..<8] 26
summ of arr[8..<12] 42
summ of arr[12..<13] 13
You can first split the array into chunks, and then add the chunks up:
extension Array {
// split array into chunks of n
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
// add each chunk up:
let results = numbers.chunked(into: 4).map { $0.reduce(0, +) }
If you would like to discard the last sum if the length of the original array is not divisible by 4, you can add an if statement like this:
let results: [Int]
if numbers.count % 4 != 0 {
results = Array(numbers.chunked(into: 4).map { $0.reduce(0, +) }.dropLast())
} else {
results = numbers.chunked(into: 4).map { $0.reduce(0, +) }
}
This is quite a basic solution and maybe not so elegant. First calculate and print sum of every group of 4 elements
var sum = 0
var count = 0
for n in stride(from: 4, to: numbers.count, by: 4) {
sum = 0
for i in n-4..<n {
sum += numbers[i]
}
count = n
print(sum)
}
Then calculate the sum of the remaining elements
sum = 0
for n in count..<numbers.count {
sum += numbers[n]
}
print(sum)
This might be rather stupid question. I would like to know if different nuances/extent of randomness would be possible using arc4random_uniform in Swift. Here's an example:
let number = arc4random_uniform(10) + 1
print(number)
In this case, a number will be printed randomly from 1 to 10. But is there a way that I can repeat the random result, 2 to 3 times? The result would be something like this:
1, 1, 6, 6, 6, 3, 3, 8, 8, 9, 9, 9 ...
// 1) Randomly selected and 2) repeated 2 to 3 times randomly.
Perhaps I might use two arc4random_uniform functions together, but cannot express them properly. Would be much appreciated if you could give me some suggestions. <3
In order to do this, you will need to generate two values: your random value and a repeatCount. Also, you'll need to remember both of those values so that you can repeat the value. You can do this with a custom class:
class RandomWithRepeats {
var range: ClosedRange<Int>
var repeatRange: ClosedRange<Int>
var repeatCount = 0
var value = 0
init(range: ClosedRange<Int>, repeatRange: ClosedRange<Int>) {
self.range = range
self.repeatRange = repeatRange
}
// generate a random number in a range
// Just use Int.random(in:) with Swift 4.2 and later
func random(in range: ClosedRange<Int>) -> Int {
return Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound + 1))) + range.lowerBound
}
func nextValue() -> Int {
// if repeatCount is 0, its time to generate a new value and
// a new repeatCount
if repeatCount == 0 {
// For Swift 4.2, just use Int.random(in:) instead
value = self.random(in: range)
repeatCount = self.random(in: repeatRange)
}
repeatCount -= 1
return value
}
}
Example:
let rand = RandomWithRepeats(range: 1...10, repeatRange: 2...3)
// generate 20 random repeated numbers
for _ in 1...20
{
print(rand.nextValue(), terminator: " ")
}
6 6 6 8 8 8 10 10 10 2 2 9 9 5 5 8 8 8 5 5
With regards to the nuances of random number generators: have a look at GKRandomSource.
What you're doing here is not really making something less random, or modifying the parameters in the random number generator. You're simply applying an operation (with one random parameter) to a collection of random integers.
extension Collection {
func duplicateItemsRandomly(range: CountableClosedRange<Int>) -> [Element] {
return self.reduce(into: [Element](), { (acc, element) in
let distance = UInt32(range.upperBound - range.lowerBound + 1)
let count = Int(arc4random_uniform(distance) + UInt32(range.lowerBound))
let result = Array.init(repeating: element, count: count)
acc.append(contentsOf: result)
})
}
}
let sequence = [1, 6, 3, 8, 9]
sequence.duplicateItemsRandomly(range: 2...3)
// [1, 1, 6, 6, 6, 3, 3, 3, 8, 8, 8, 9, 9, 9]
P.S: If you're writing this code in Swift 4.2, please use Int.random(in:).
I'd suggest a custom Sequence:
class RepeatingRandomSequence : Sequence {
let rangeLow, rangeSpan : UInt32
let repeatLow, repeatSpan : UInt32
init(range:Range<UInt32>, count:Range<UInt32>) {
rangeLow = range.lowerBound
rangeSpan = range.upperBound - range.lowerBound + 1
repeatLow = count.lowerBound
repeatSpan = count.upperBound - count.lowerBound + 1
}
func makeIterator() -> AnyIterator<UInt32> {
var count : UInt32 = 0
var value : UInt32 = 0
return AnyIterator {
if(count <= 0) {
count = arc4random_uniform(self.repeatSpan) + self.repeatLow
value = arc4random_uniform(self.rangeSpan) + self.rangeLow
}
defer { count = count - 1 }
return value
}
}
}
let sequence = RepeatingRandomSequence(range: 0..<10, count: 2..<3)
let randoms = sequence.makeIterator()
Note that the iterator, randoms now generates an endless sequence of random numbers using randoms.next() Since the sequence is endless, many things aren't particularly useful, like sort, map, etc. You could however use it like:
for value in random {
print(value)
if(value == 9) { // or any other termination condition
break
}
}
Or more conventionally, as:
(0..<10).forEach { _ in
print(String(describing: random.next()))
}
I got a simple code which works and which I am programming in and old fashioned way and I am sure there is a more elegant way of doing this in swift. Here is the code:
var cardsInCompartment1:Int = 0
var cardsInCompartment2:Int = 0
for card in cards{
if card.compartment == 1{
cardsInCompartment1 += 1
print(cardsInCompartment1)
}
if card.compartment == 2{
cardsInCompartment2 += 1
print(cardsInCompartment2)
}
}
I basically got cards in different compartments and now I want to count how many cards are in each compartment.
How about using filter to select the cards you want? Then you can just count them:
let cardsInCompartment1 = cards.filter { $0.compartment == 1 }.count
let cardsInCompartment2 = cards.filter { $0.compartment == 2 }.count
If you have a bunch of compartments, you could store the counts in a dictionary:
var compartmentCounts = [Int:Int]()
cards.forEach {
compartmentCounts[$0.compartment] = (compartmentCounts[$0.compartment] ?? 0) + 1
}
In this case, the key would be the compartment#, and the value would be the card count. Something like [1: 32, 2: 42] if there are 32 and 42 cards in each respective compartment.
Try this:
var cardsInCompartment1:Int = 0
var cardsInCompartment2:Int = 0
for card in cards {
(card.compartment == 1) ? (cardsInCompartment1 += 1) : (cardsInCompartment2 += 1)
}
I think you should store the cardsInCompartment as arrays:
var cardsInCompartment = [0, 0] // you can add more to this array
Then you can just loop through the cards and add the values to the array elements:
for card in cards {
cardsInCompartment[card.compartment - 1] += 1
print(cardsInCompartment[card.compartment - 1])
}
What about a switch statement? Something like this?
var card:Int = 1
var CardsInCompartment:Int = 0
switch (card) {
case 1:
CardsInCompartment += 1
print("CardsInCompartment \(CardsInCompartment)")
case 2:
CardsInCompartment += 2
print("CardsInCompartment \(CardsInCompartment)")
default:
}
Or, use an Array to keep your counts:
var counts = [ 0, 0, 0 ] // create an array of integers, where the Ints in the array represent the count of cards in each compartment
cards.forEach { counts[ $0.compartment ] += 1 } // for each card, increment the count in array corresponding to the compartment of the card. (if card.compartment == 1, increment counts[1], and so on
print("cards in compartment 1 \(counts[1])")
print("cards in compartment 2 \(counts[2])")
(This assumes your only compartments are integers 1 and 2)
I like Aaron Brager's idea which counts values into dictionary. I am using reduce to eliminate mutable dictionary outside the 'loop' (more functional)
let d = cards.reduce([:]) { (d, card) -> [Int:Int] in
var d = d
let s = d[card.compartment] ?? 0
d[card.compartment] = s + 1
return d
}
I am trying to solve the second problem on Project Euler. The problem is as follows:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
I think I've written a solution, but when I try to run my code it crashes my Swift playground and gives me this error message:
Playground execution aborted: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
var prev = 0
var next = 1
var num = 0
var sum = 0
for var i = 1; i < 400; i++ {
num = prev + next
if next % 2 == 0 {
sum += next
}
prev = next
next = num
}
print(sum)
The weird thing is, if I set the counter on my loop to less than 93, it works fine. Explicitly setting the variable names to Double does not help. Anyone know what's going on here?
There is nothing weird about this at all. Do you know how large the 400 fibonacci number is?
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
Swift Int64 or UInt64 simply cannot handle that large of a number. The later can go up to 18446744073709551615 at max - not even close.
If you change your variables to be doubles it works but will be inaccurate:
var prev : Double = 0
var next : Double = 1
var num : Double = 0
var sum : Double = 0
will yield
2.84812298108489e+83
which is kind of close to the actual value of
1.76e+83
Luckily you do not need to get values that big. I would recommend not writing a for loop but a while loop that calculates the next fibonacci number until the break condition is met whose values do not exceed four million.
The Fibonacci numbers become very large quickly. To compute large Fibonacci numbers, you need to implement some kind of BigNum. Here is a version the makes a BigNum that is implemented internally as an array of digits. For example, 12345 is implemented internally as [1, 2, 3, 4, 5]. This makes it easy to represent arbitrarily large numbers.
Addition is implemented by making the two arrays the same size, then map is used to add the elements, finally the carryAll function restores the array to single digits.
For example 12345 + 67:
[1, 2, 3, 4, 5] + [6, 7] // numbers represented as arrays
[1, 2, 3, 4, 5] + [0, 0, 0, 6, 7] // pad the shorter array with 0's
[1, 2, 3, 10, 12] // add the arrays element-wise
[1, 2, 4, 1, 2] // perform carry operation
Here is the implementation of BigNum. It is also CustomStringConvertible which makes it possible to print the result as a String.
struct BigNum: CustomStringConvertible {
var arr = [Int]()
// Return BigNum value as a String so it can be printed
var description: String { return arr.map(String.init).joined() }
init(_ arr: [Int]) {
self.arr = carryAll(arr)
}
// Allow BigNum to be initialized with an `Int`
init(_ i: Int = 0) {
self.init([i])
}
// Perform the carry operation to restore the array to single
// digits
func carryAll(_ arr: [Int]) -> [Int] {
var result = [Int]()
var carry = 0
for val in arr.reversed() {
let total = val + carry
let digit = total % 10
carry = total / 10
result.append(digit)
}
while carry > 0 {
let digit = carry % 10
carry = carry / 10
result.append(digit)
}
return result.reversed()
}
// Enable two BigNums to be added with +
static func +(_ lhs: BigNum, _ rhs: BigNum) -> BigNum {
var arr1 = lhs.arr
var arr2 = rhs.arr
let diff = arr1.count - arr2.count
// Pad the arrays to the same length
if diff < 0 {
arr1 = Array(repeating: 0, count: -diff) + arr1
} else if diff > 0 {
arr2 = Array(repeating: 0, count: diff) + arr2
}
return BigNum(zip(arr1, arr2).map { $0 + $1 })
}
}
// This function is based upon this question:
// https://stackoverflow.com/q/52975875/1630618
func fibonacci(to n: Int) {
guard n >= 2 else { return }
var array = [BigNum(0), BigNum(1)]
for i in 2...n {
array.append(BigNum())
array[i] = array[i - 1] + array[i - 2]
print(array[i])
}
}
fibonacci(to: 400)
Output:
1
2
3
5
8
...
67235063181538321178464953103361505925388677826679492786974790147181418684399715449
108788617463475645289761992289049744844995705477812699099751202749393926359816304226
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
Hy,
I have a very Basic Question which is :
How can i create a random number with 20 digits no floats no negatives (basically an Int) in Swift ?
Thanks for all answers XD
Step 1
First of all we need an extension of Int to generate a random number in a range.
extension Int {
init(_ range: Range<Int> ) {
let delta = range.startIndex < 0 ? abs(range.startIndex) : 0
let min = UInt32(range.startIndex + delta)
let max = UInt32(range.endIndex + delta)
self.init(Int(min + arc4random_uniform(max - min)) - delta)
}
}
This can be used this way:
Int(0...9) // 4 or 1 or 1...
Int(10...99) // 90 or 33 or 11
Int(100...999) // 200 or 333 or 893
Step 2
Now we need a function that receive the number of digits requested, calculates the range of the random number and finally does invoke the new initializer of Int.
func random(digits:Int) -> Int {
let min = Int(pow(Double(10), Double(digits-1))) - 1
let max = Int(pow(Double(10), Double(digits))) - 1
return Int(min...max)
}
Test
random(1) // 8
random(2) // 12
random(3) // 829
random(4) // 2374
Swift 5: Simple Solution
func random(digits:Int) -> String {
var number = String()
for _ in 1...digits {
number += "\(Int.random(in: 1...9))"
}
return number
}
print(random(digits: 1)) //3
print(random(digits: 2)) //59
print(random(digits: 3)) //926
Note It will return value in String, if you need Int value then you can do like this
let number = Int(random(digits: 1)) ?? 0
Here is some pseudocode that should do what you want.
generateRandomNumber(20)
func generateRandomNumber(int numDigits){
var place = 1
var finalNumber = 0;
for(int i = 0; i < numDigits; i++){
place *= 10
var randomNumber = arc4random_uniform(10)
finalNumber += randomNumber * place
}
return finalNumber
}
Its pretty simple. You generate 20 random numbers, and multiply them by the respective tens, hundredths, thousands... place that they should be on. This way you will guarantee a number of the correct size, but will randomly generate the number that will be used in each place.
Update
As said in the comments you will most likely get an overflow exception with a number this long, so you'll have to be creative in how you'd like to store the number (String, ect...) but I merely wanted to show you a simple way to generate a number with a guaranteed digit length. Also, given the current code there is a small chance your leading number could be 0 so you should protect against that as well.
you can create a string number then convert the number to your required number.
func generateRandomDigits(_ digitNumber: Int) -> String {
var number = ""
for i in 0..<digitNumber {
var randomNumber = arc4random_uniform(10)
while randomNumber == 0 && i == 0 {
randomNumber = arc4random_uniform(10)
}
number += "\(randomNumber)"
}
return number
}
print(Int(generateRandomDigits(3)))
for 20 digit you can use Double instead of Int
Here is 18 decimal digits in a UInt64:
(Swift 3)
let sz: UInt32 = 1000000000
let ms: UInt64 = UInt64(arc4random_uniform(sz))
let ls: UInt64 = UInt64(arc4random_uniform(sz))
let digits: UInt64 = ms * UInt64(sz) + ls
print(String(format:"18 digits: %018llu", digits)) // Print with leading 0s.
16 decimal digits with leading digit 1..9 in a UInt64:
let sz: UInt64 = 100000000
let ld: UInt64 = UInt64(arc4random_uniform(9)+1)
let ms: UInt64 = UInt64(arc4random_uniform(UInt32(sz/10)))
let ls: UInt64 = UInt64(arc4random_uniform(UInt32(sz)))
let digits: UInt64 = ld * (sz*sz/10) + (ms * sz) + ls
print(String(format:"16 digits: %llu", digits))
Swift 3
appzyourlifz's answer updated to Swift 3
Step 1:
extension Int {
init(_ range: Range<Int> ) {
let delta = range.lowerBound < 0 ? abs(range.lowerBound) : 0
let min = UInt32(range.lowerBound + delta)
let max = UInt32(range.upperBound + delta)
self.init(Int(min + arc4random_uniform(max - min)) - delta)
}
}
Step 2:
func randomNumberWith(digits:Int) -> Int {
let min = Int(pow(Double(10), Double(digits-1))) - 1
let max = Int(pow(Double(10), Double(digits))) - 1
return Int(Range(uncheckedBounds: (min, max)))
}
Usage:
randomNumberWith(digits:4) // 2271
randomNumberWith(digits:8) // 65273410
Swift 4 version of Unome's validate response plus :
Guard it against overflow and 0 digit number
Adding support for Linux's device because "arc4random*" functions don't exit
With linux device don't forgot to do
#if os(Linux)
srandom(UInt32(time(nil)))
#endif
only once before calling random.
/// This function generate a random number of type Int with the given digits number
///
/// - Parameter digit: the number of digit
/// - Returns: the ramdom generate number or nil if wrong parameter
func randomNumber(with digit: Int) -> Int? {
guard 0 < digit, digit < 20 else { // 0 digit number don't exist and 20 digit Int are to big
return nil
}
/// The final ramdom generate Int
var finalNumber : Int = 0;
for i in 1...digit {
/// The new generated number which will be add to the final number
var randomOperator : Int = 0
repeat {
#if os(Linux)
randomOperator = Int(random() % 9) * Int(powf(10, Float(i - 1)))
#else
randomOperator = Int(arc4random_uniform(9)) * Int(powf(10, Float(i - 1)))
#endif
} while Double(randomOperator + finalNumber) > Double(Int.max) // Verification to be sure to don't overflow Int max size
finalNumber += randomOperator
}
return finalNumber
}