add a range of objects from one array to another - ios

Currently we have 2 arrays:
fileprivate var totalDrinksArray: [CocktailModel] = []
fileprivate var currentDrinksArray: [CocktailModel] = []
What I'm trying to do is get the first 2 objects of the totalDrinksArray and add them to the currentDrinksArray. After a button is pressed the next 2 drinks will be added from the totalDrinksArray to the currentDrinksArray (for a total of 4 drinks )and so on.

You can use Array method func prefix(_ maxLength: Int) which will return a slice of the total array (up to n elements) or less if there is not enough elements and append the contents to your current array or insert it at the desired index:
currentDrinksArray.append(contentsOf: totalDrinksArray.prefix(2))
Or if you would like to insert them in the beginning of your array:
currentDrinksArray.insert(contentsOf: totalDrinksArray.prefix(2), at: 0)

You can "add" arrays together:
currentDrinksArray += totalDrinksArray[0...1]
should work.
Not quite clear from your question, but if you want to "add the next two" (the 3rd and 4th):
currentDrinksArray += totalDrinksArray[2...3]

Simplest safe solution IMO:
var currentIndex = 0
func addDrinks() {
if(currentIndex + 2 >= totalDrinksArray.count) {
currentDrinksArray += totalDrinksArray[currentIndex...]
}
else {
currentDrinksArray += totalDrinksArray[currentIndex..<(currentIndex + 2)]
}
currentIndex += 2
}

Related

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 count occurrences of an element in a Swift array of objects with different conditions

I am doing a test on the content of an element's attribute in order calculate occurrences
for a simple array of strings for example this example works just fine when I want to test with one condition:
counts[item.id] = (counts[item.id] ?? 0) + 1
however my question is, how to do the same test with different conditions, by not only testing the item.id, I would like to test also with item.email for example. is it something like this
counts[item.id] = (counts[item.id] && counts[item.email] = (counts[item.email] ?? 0) + 1
this is the array in which I am adding key values, key as elements of my object, and values as number of occurrences
here is a clear example
let arr = [Student, Student, Student, Student]
var counts: [String: Int] = [:]
for item in arr {
counts[item.id] = (counts[item.id] ?? 0) + 1
}
for (key, value) in counts {
print("\(key) occurs \(value) time(s)")
}
output:
44442 occurs 1 time(s)
34434 occurs 1 time(s)
22222 occurs 2 time(s)
thank you for you help.
the answer was very simple it was in the extension of my class and by adding the Equatable,
extension notificationManager: Equatable {
static func ==(lhs: notificationManager, rhs: notificationManager) -> Bool {
return lhs.fromwho.username == rhs.fromwho.username && lhs.forwho.username == rhs.forwho.username && lhs.activity.objectId == rhs.activity.objectId
}}
and then i just add the count
for item in orignalNotifs {
notificationManager.Notifcounts[item] = (notificationManager.Notifcounts[item] ?? 0) + 1
}

How can I perform an Array Slice in Swift?

var mentions = ["#alex", "#jason", "#jessica", "#john"]
I want to limit my array to 3 items, so I want to splice it:
var slice = [String]()
if mentions.count > 3 {
slice = mentions[0...3] //alex, jason, jessica
} else {
slice = mentions
}
However, I'm getting:
Ambiguous subscript with base type '[String]' and index type 'Range'
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
Target: x86_64-apple-macosx10.9
The problem is that mentions[0...3] returns an ArraySlice<String>, not an Array<String>. Therefore you could first use the Array(_:) initialiser in order to convert the slice into an array:
let first3Elements : [String] // An Array of up to the first 3 elements.
if mentions.count >= 3 {
first3Elements = Array(mentions[0 ..< 3])
} else {
first3Elements = mentions
}
Or if you want to use an ArraySlice (they are useful for intermediate computations, as they present a 'view' onto the original array, but are not designed for long term storage), you could subscript mentions with the full range of indices in your else:
let slice : ArraySlice<String> // An ArraySlice of up to the first 3 elements
if mentions.count >= 3 {
slice = mentions[0 ..< 3]
} else {
slice = mentions[mentions.indices] // in Swift 4: slice = mentions[...]
}
Although the simplest solution by far would be just to use the prefix(_:) method, which will return an ArraySlice of the first n elements, or a slice of the entire array if n exceeds the array count:
let slice = mentions.prefix(3) // ArraySlice of up to the first 3 elements
We can do like this,
let arr = [10,20,30,40,50]
let slicedArray = arr[1...3]
if you want to convert sliced array to normal array,
let arrayOfInts = Array(slicedArray)
You can try .prefix().
Returns a subsequence, up to the specified maximum length, containing the initial elements of the collection.
If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.
let numbers = [1, 2, 3, 4, 5]
print(numbers.prefix(2)) // Prints "[1, 2]"
print(numbers.prefix(10)) // Prints "[1, 2, 3, 4, 5]"
General solution:
extension Array {
func slice(size: Int) -> [[Element]] {
(0...(count / size)).map{Array(self[($0 * size)..<(Swift.min($0 * size + size, count))])}
}
}
Can also look at dropLast() function:
var mentions:[String] = ["#alex", "#jason", "#jessica", "#john"]
var slice:[String] = mentions
if mentions.count > 3 {
slice = Array(mentions.dropLast(mentions.count - 3))
}
//print(slice) => ["#alex", "#jason", "#jessica"]
I came up with this:
public extension Array {
func slice(count: Int) -> [some Collection] {
let n = self.count / count // quotient
let i = n * count // index
let r = self.count % count // remainder
let slices = (0..<n).map { $0 * count }.map { self[$0 ..< $0 + count] }
return (r > 0) ? slices + [self[i..<i + r]] : slices
}
}
You can also slice like this:
//Generic Method
func slice<T>(arrayList:[T], limit:Int) -> [T]{
return Array(arrayList[..<limit])
}
//How to Use
let firstThreeElements = slice(arrayList: ["#alex", "#jason", "#jessica", "#john"], limit: 3)
Array slice func extension:
extension Array {
func slice(with sliceSize: Int) -> [[Element]] {
guard self.count > 0 else { return [] }
var range = self.count / sliceSize
if self.count.isMultiple(of: sliceSize) {
range -= 1
}
return (0...range).map { Array(self[($0 * sliceSize)..<(Swift.min(($0 + 1) * sliceSize, self.count))]) }
}
}

Randomly choosing an item from a Swift array without repeating

This code picks a random color from a array of pre-set colors. How do I make it so the same color doesn't get picked more than once?
var colorArray = [(UIColor.redColor(), "red"), (UIColor.greenColor(), "green"), (UIColor.blueColor(), "blue"), (UIColor.yellowColor(), "yellow"), (UIColor.orangeColor(), "orange"), (UIColor.lightGrayColor(), "grey")]
var random = { () -> Int in
return Int(arc4random_uniform(UInt32(colorArray.count)))
} // makes random number, you can make it more reusable
var (sourceColor, sourceName) = (colorArray[random()])
Create an array of indexes. Remove one of the indexes from the array and then use that to fetch a color.
Something like this:
var colorArray = [
(UIColor.redColor(), "red"),
(UIColor.greenColor(), "green"),
(UIColor.blueColor(), "blue"),
(UIColor.yellowColor(), "yellow"),
(UIColor.orangeColor(), "orange"),
(UIColor.lightGrayColor(), "grey")]
var indexes = [Int]();
func randomItem() -> UIColor
{
if indexes.count == 0
{
print("Filling indexes array")
indexes = Array(0..< colorArray.count)
}
let randomIndex = Int(arc4random_uniform(UInt32(indexes.count)))
let anIndex = indexes.removeAtIndex(randomIndex)
return colorArray[anIndex].0;
}
The code above creates an array indexes. The function randomItem looks to see if indexes is empty. if it is, it populates it with index values ranging from 0 to colorArray.count - 1.
It then picks a random index in the indexes array, removes the value at that index in the indexes array, and uses it to fetch and return an object from your colorArray. (It doesn't remove objects from the colorArray. It uses indirection, and removes objects from the indexesArray, which initially contains an index value for each entry in your colorArray.
The one flaw in the above is that after you fetch the last item from indexArray, you populate it with a full set of indexes, and it's possible that the next color you get from the newly repopulated array will be the same as the last one you got.
It's possible to add extra logic to prevent this.
based on the fact, that arc4random_uniform generate not only random, but also uniformly distributed numbers
import Foundation // arc4random_uniform
class Random {
var r:UInt32
let max: UInt32
init(max: UInt32) {
self.max = max
r = arc4random_uniform(max)
}
var next: UInt32 {
var ret: UInt32
repeat {
ret = arc4random_uniform(max)
} while r == ret
r = ret
return r
}
}
// usage example
let r = Random(max: 5)
for i in 0..<10 {
print(r.r, r.next) // there will never be a pair of the same numbers in the
// generated stream
}
/*
2 4
4 0
0 3
3 0
0 3
3 4
4 1
1 3
3 4
4 3
*/
simple test for different k and stream length of one milion
class Random {
var r:UInt32
let max: UInt32
init(max: UInt32) {
self.max = max
r = arc4random_uniform(max)
}
var next: (UInt32, Int) {
var i = 0
var ret: UInt32
repeat {
ret = arc4random_uniform(max)
i += 1
} while r == ret
r = ret
return (r,i)
}
}
for k in 3..<16 {
let r = Random(max: UInt32(k))
var repetition = 0
var sum = 0
for i in 0..<1000000 {
let j = r.next
repetition = max(repetition, j.1)
sum += j.1
}
print("maximum of while repetition for k:", k, "is", repetition, "with average of", Double(sum) / Double(1000000) )
}
prints
maximum of while repetition for k: 3 is 15 with average of 1.499832
maximum of while repetition for k: 4 is 12 with average of 1.334008
maximum of while repetition for k: 5 is 9 with average of 1.250487
maximum of while repetition for k: 6 is 8 with average of 1.199631
maximum of while repetition for k: 7 is 8 with average of 1.167501
maximum of while repetition for k: 8 is 7 with average of 1.142799
maximum of while repetition for k: 9 is 8 with average of 1.124096
maximum of while repetition for k: 10 is 6 with average of 1.111178
maximum of while repetition for k: 11 is 7 with average of 1.099815
maximum of while repetition for k: 12 is 7 with average of 1.091041
maximum of while repetition for k: 13 is 6 with average of 1.083582
maximum of while repetition for k: 14 is 6 with average of 1.076595
maximum of while repetition for k: 15 is 6 with average of 1.071965
finaly, here is more Swifty and functional approach based on the same idea
import Foundation
func random(max: Int)->()->Int {
let max = UInt32(max)
var last = arc4random_uniform(max)
return {
var r = arc4random_uniform(max)
while r == last {
r = arc4random_uniform(max)
}
last = r
return Int(last)
}
}
let r0 = random(8)
let r1 = random(4)
for i in 0..<20 {
print(r0(), terminator: " ")
}
print("")
for i in 0..<20 {
print(r1(), terminator: " ")
}
/*
4 5 4 3 4 0 5 6 7 3 6 7 5 4 7 4 7 2 1 6
0 3 0 1 0 2 3 1 2 0 1 0 1 0 1 3 0 3 0 2
*/
Fill an array with the colors and shuffle it with a Fisher-Yates shuffle. Then use the element at an end, remove it, and insert it at a random position at least n positions from the end.
For example, say my array has 10 elements. I shuffle it and take the last. I want at least 2 values to be chosen before I see it again so I generate a random position in the range 0...8 and insert it there.
var colorArray = [
(UIColor.redColor() , "red" ),
(UIColor.greenColor() , "green" ),
(UIColor.blueColor() , "blue" ),
(UIColor.yellowColor() , "yellow"),
(UIColor.orangeColor() , "orange"),
(UIColor.lightGrayColor(), "grey" )].shuffle() // shuffle() is from my link above
let spacing = 2 // Pick at least 2 colors before we see it again
if let randomColor = colorArray.popLast() {
colorArray.insert(randomColor,
atIndex: Int(arc4random_uniform(UInt32(colorArray.count - spacing))))
}
One case, described here: https://github.com/dimpiax/GenericSequenceType
Another is functional:
func getRandomItem<T>(arr: [T]) -> (unique: Bool) -> T {
var indexes: [Int]!
return { value in
let uniqIndex: Int
if value {
if indexes?.isEmpty != false {
indexes = [Int](0.stride(to: arr.count, by: 1))
}
uniqIndex = indexes.removeAtIndex(Int(arc4random_uniform(UInt32(indexes.count))))
}
else {
uniqIndex = Int(arc4random_uniform(UInt32(arr.count)))
}
return arr[uniqIndex]
}
}
let generate = getRandomItem(colorArray)
generate(unique: true).0 // greenColor
generate(unique: true).0 // redColor
generate(unique: true).0 // lightGrayColor
Try it it's work for me and 100% tested
let arrString = ["1","2","3","4","5","6"]
var selectedIndix = -1
#IBAction func btnClick(_ sender: Any) {
let randomElementIndex = randomElementString()
}
Call this function when button clicked
func randomElementString() -> Int{
let randomm = Int(arc4random_uniform(UInt32(arrString.count)))
if selectedIndix == randomm{
return randomElementString()
}else{
selectedIndix = randomm
return randomm
}
}
OUTPUT:-
5121242316513126
How about running a while loop with the condition:
while(self.source.backgroundColor == sourceColor) {
// get a new random sourceColor
}
This will keep looping until a new random color has been selected.
edit
Additional Note: The point was the while loop. There are ways to safeguard from an infinite loop, it's up to the coder to find the right solution. I don't think SO is a place to write other's code but instead to offer suggestions .. mine is a start.
But since my answer was given such a negative rating, i'll push instead of nudge in the right direction.
The other answers are unnecessarily bloated. And? The one I offered above offers a less than desirable time complexity. So, here's my new answer (in meta code):
// array of all background colors
var arrayOfColors = [..]
// get a random index
var randomIndex = arc4random(size of arrayOfColors)
// select new background color
var newBGColor = arrayOfColors[randomIndex]
// old background color
var oldBGColor = self.source.backgroundColor
// remove new color from array (so that it's excluded from choices next time)
arrayOfColors.removeAtIndex(randomIndex)
// set the new color to the background
self.source.backgroundColor = newBGColor
// add current color back into the pool of potential colors
arrayOfColors.addObject(oldBGColor)

Create two arrays and then use them in a third array used as data for a two spinner uipickerview

Hi I'm trying to create my first IOS app which uses a uiPickerView with two spinners.
From various tutorials I found that the picker data was defined so:
var pickerData = [["value1","value2", "value3"],["valueA", "valueB"]]
As I have a number of values I thought I would create them dynamically
The code I have is:
var poundValues = [String]()
var penceValues = [String]()
for var indexP:Int = 0; indexP < 100; indexP += 1 {
poundValues.append("£ \(indexP)")
}
for var pindex:Int = 0; pindex < 100; pindex += 1 {
penceValues.append(".\(pindex)")
}
let pickerData = [poundValues,penceValues]
Unfortunately I get compiler error and I can't figure out how to correct. The first error occurs on both the "for var" lines...Consecutive declarations on a line must be separated by ';' but I do have ;s in place.
The second is on the "let pickerData" line...'ViewController.Type' does not have a member named 'poundValues'
Also in the penceValues I'd like to pad single digits with a leading 0 so that the spinner displays .00 .01 .02 .... .09 .10 .11 etc.
Any help appreciated.
The code you provided compiles fine as is, although you can save a second loop since both for loops iterate over the same indices. Also, below is the formatting to pad your single digits.
var poundValues = [String]()
var penceValues = [String]()
for var indexP:Int = 0; indexP < 100; indexP += 1 {
poundValues.append("£ \(indexP)")
penceValues.append(NSString(format: ".%02d", indexP) as String)
}
let pickerData = [poundValues, penceValues]
It would be helpful to see your UIViewController subclass in its entirety to help with the build errors you are seeing.
I don't have those errors after pasting your code in a playground "as is", so I presume there's something wrong in the code surrounding it - unclosed bracket maybe?
As for your code, you can get rid of the 2 for loops, and simplify it as follows:
let poundValues = map(0..<100) { "£ \($0)" }
let penceValues = map(0..<100) { ($0 < 10 ? "0" : "") + "\($0)" }
The penceValues array contains the first 10 elements with an additional 0 prefix.
I don't know what causes your code to break, but wanted to show you an alternative solution. You don't have to create the arrays at all. I assume you are using the array values in the titleForRow method. Instead of using an array, you can generate the values in there:
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return "£ \(row)"
}
else {
return String(format: ".%.02d", arguments: [row])
}
}

Resources