(Swift) moving objects while iterating over array - ios

I am trying to remove some objects from 1 array, and move them to another.
I am doing this by removing them from a reversed array, and adding them to another array, like so:
var array1 = [1,2,3,4,5,6]
var array2 = [1,2]
for (index, number) in array1.enumerated().reversed() {
if(number>2) {
array1.remove(at: index)
array2.append(number)
}
}
The problem is, the objects in array 2 are obviously reversed (1,2,6,5,4,3)
I can easily come up with complicated workarounds, but I was wondering if there are any straightforward ways of doing this.
Thanks in advance!

Rather than appending the numbers insert them
array2.insert(number, at: 2)
You can do the same thing without a loop
let droppedItems = array1.dropFirst(2)
array1.removeLast(array1.count - 2)
array2.append(contentsOf: droppedItems)

If I understand you correctly, you want to move numbers from array1 to array2 if they are higher than 2:
// get only numbers higher than 2 and append them to the second array
array2.append(contentsOf: array1.filter { $0 > 2 })
// filter the moved items from the first array
array1 = array1.filter { $0 <= 2 }
or
// split the array into two parts in place
let index = array1.partition { $0 > 2 }
// move the second part
array2 += array1[index...]
// remove the second part
array1.removeSubrange(index...)

Reverse it, grab subarray then append to array2. You don't need to mutate array1. Something like:
array2.append(contentsOf: array1.reversed()[0..<array1.count-1])

Related

Swift: how to always get different value from shuffled array [duplicate]

This question already has answers here:
Get a random unique element from an Array until all elements have been picked in Swift
(5 answers)
Closed 3 years ago.
I created an array of words basically inside of a button, and each time I press my button I get a random item from the array. Now sometimes I get identical items. What if I don't want my items to repeat themselves and I always want to get new items? (Obviously I even want them to repeat their cycle after they all have ben showed once).
#IBOutlet weak var shoppingLabel : UILabel!
#IBAction func shoppingListButton(_ sender: Any) {
var shoppingList = ["Oranges","Apples","Broccoli"].shuffled()
print(shoppingList)
resultLabel.text = shoppingList.first ?? ""
}
this is not a duplicate as the similar question has an array outside of the button and is a var array, mine is a let. With my array I'm unable to remove items from it because it can't be changed, and no, I can't make it a var array...
To cycle through a random array:
Create the array
Shuffle it once
Pick values by cycling through the array from the beginning to the end
To achieve 1) and 2) simply define the array as a constant and shuffle it outside the method in which you want to use it.
To achieve 3) create an additional variable to keep track of which index of the array you are currently at, and increment it after picking a value.
To make sure you don't go beyond the bounds of the array and to achieve the "cycling" through of the array, reset the index to 0 when the index becomes greater than the last index of the array. A simple way to do this, is to use the remainder operator % in Swift.
E.g.
let shoppingList = ["Oranges", "Apples", "Broccoli"].shuffled()
var currentIndex = 0
#IBAction func shoppingListButton(_ sender: Any) {
// pick an item
let nextItem = shoppingList[currentIndex]
// update the label
resultLabel.text = nextItem
// increment the index to cycle through items
currentIndex = (currentIndex + 1) % shoppingList.count
}
To pick random non-repeating values from an array:
Create the array
Pick a random value from the array
If the picked value equals the last value, pick a new one
To achieve 2) use the randomElement() function to pick a random element. This is less computationally expensive than shuffling the entire array and picking the first element each time.
To achieve 3) use a while loop or similar to keep picking random elements until a new one is generated.
E.g.
let shoppingList = ["Oranges", "Apples", "Broccoli"]
#IBAction func shoppingListButton(_ sender: Any) {
// pick a random element that is not equal to the last one
var nextItem: String?
repeat {
nextItem = shoppingList.randomElement()
} while nextItem == resultLabel.text
// update the label
resultLabel.text = nextItem
}

Filter an array by NOT containing string (swift 2)

I am relatively new to swift; I am working on filtering arrays.
I know how to filter out elements of an array that contain a letter (like so: let filteredList = wordlist.filter { !$0.characters.contains(letter) }), but how do I filter out elements that do not have a letter?
Here's what I want to accomplish:
I have a word list in string-array format, i.e. ["thing", "other thing"] (but much longer), and I want to return every element that has a certain letter, filtering out the ones that do not have a certain letter.
Thanks in advance.
This was a silly question, I am sorry. Anyway, I just needed to remove the exclamation mark. So...
let filteredList = wordlist.filter { !$0.characters.contains(letter) }
// returns elements in the array WITHOUT "letter".
let filteredList = wordlist.filter { $0.characters.contains(letter) }
// returns elements in the array WITH "letter".
Thanks Eendje.

Swift add all elements from array together

I have an array of numbers and I want to iterate through all of the elements in that array and add together all of the integers. Here is the function I have so far:
func addTogether(array:Array<Int>, divide:Int) -> Int
{
var a = 0
while a < array.count
{
}
return 0
}
I know that I'm probably going to have to do this inside of the while loop. Can anyone give me some guidance as to where to go from here? Thanks!
No loop needed. Use reduce, like this:
let sum = array.reduce(0,+)

How Can I Remove Only Selected Index Paths From An Array In Swift?

I have a mutable array:
var responseArray = ["yes", "no", "no way", "of course", "for sure", "not a chance", "positively"]
The responseArray is the data source for my table view which allows for multiple selections during editing.
I am capturing the selected index paths:
let paths = tableView.indexPathsForSelectedRows()
I can return and verify each selected indexPath of my tableView by running println(paths).
I have read the documentation for the indexPathsForSelectedRows method and understand that it returns an array of index paths which I have sorted by row.
What I cannot understand is how I can use the returned array of index paths to remove the data from the responseArray for each row that is selected for deletion in the table view.
After reading through some documents, is it correct of me to believe that I cannot remove any data from the `responseArray' as I am enumerating over it? For example:
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row)
}
self.tableView.reloadData()
}
}
}
I am able to remove each row from the table view one by one, but when I select the first and last rows, all of the rows, or any other combination of rows for deletion, I get fatal error: Array index out of range. However, if I select two adjacent rows for deletion, the desired result is achieved and those two rows are removed from the table view.
I know that there is something that I am missing, but as a new programmer I have been unable to resolve this issue for three days now. What is it that I am not doing correctly?
Here's your array: [ A, B, C, D ]
Let's say you want to delete A and D at index 0 and 3 respectively, one at a time:
deleteAtIndex(0) gives: [ B, C, D ]
deleteAtIndex(3) gives: Out of bounds exception
Edit:
Ok, to avoid complicating things, why not just always delete the highest index first by reversing your sort: {$1.row < $0.row}
For future reference, in addition to the answers given already you could simplify it further by reversing the selected indices:
#IBAction func deleteButtonPressed(sender: AnyObject) {
self.tableView.selectedRowIndexes.reverse().forEach { x in
responseArray.removeAtIndex(x)
}
self.tableView.reloadData()
}
Since your paths array is sorted, you know that every time you delete an element from the array, higher indices will now be one less than they were. You can simply keep an incrementing offset to apply to your deletions -
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
var offset=0;
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row-offset)
offset++
}
self.tableView.reloadData()
}
}
}

Swift: how do I concatenate a [[String]] to a [String] the right way?

There are new Array functions in swift - map, reduce etc.
I'd like to use these to concatenate a [[String]] to a [String] but I can't figure out how. (I'm assuming map or reduce would do what I want but could be wrong).
What is the best way to do what I need to?
You'll want to use reduce for that, since you're basically going from an array of T down to T, it's just that T = [String].
let stringArray = stringArrayArray.reduce([]) { $0 + $1 }
You can write it even more concisely by using operator shorthand instead of a closure:
let stringArray = stringArrayArray.reduce([], +)
And here's the full way to write it out, so you can see what's happening:
1: let stringArray = stringArrayArray.reduce([]) {
2: (accumulated: [String], element: [String]) -> [String] in
3: return accumulated + element
4: }
In line 1, we provide an empty array for the initial value. Line 2 defines the arguments and return type of the closure: accumulated is the initial value in the first iteration, and the result of the previous iteration thereafter, while element is the current element of the source array. Line 3 simply adds the accumulated array with the current element to merge them together.

Resources