Replacing objects in one array to another array, randomly. Swift 4 - arrays

Two arrays
let array1 = ["A","B","C","D","E","F","G"]
let array2 = ["a","b","c","d","e","f","g"]
I want to choose an index from array2 and replace the object in array1 at the same index with the object from array2. For example
array1[3] = array2[3] //["A","B","C","d","E","F","G"]
I want to do this randomly, for example
let randomIndex: Int = Int(arc4random()) % (array2.count)
I want to do this in a 'for' loop until all the indices and objects for array2 are used, but I DON'T WANT TO REPEAT a randomIndex.
If I decrement the number of objects after each iteration, I still could get the same random index. If I use a Set of indices, and remove the used index, I lose my 'orderedness' (if that's a word).
So I seem to be stuck. BTW swift 4's .randomElement won't work on an array of Strings.
Any thoughts?
for object in array2 {
let randomIndex: Int = Int(arc4random()) % (array2.count)
array1[randomIndex] = array2[randomIndex]
array2.remove(at: randomIndex)
}
The above does NOT work as I desire. When the object is removed, a new order is established, and I can't replace the object in array1 at the appropriate index.
I'm missing something obvious, but I'm not seeing it. I'm using Swift 4.2.

This seems very inefficient, but works:
let array1 = ["A","B","C","D","E","F","G"]
let array2 = ["a","b","c","d","e","f","g"]
var array3 = array1 // ["A","B","C","D","E","F","G"]
var counterSet = Set<Int>() // empty set
while counterSet.count < array2.count {
let randomIndex: Int = Int(arc4random()) % (array2.count) //(16 times)
counterSet.insert(randomIndex) //(16 times)
array3[randomIndex] = array2[randomIndex] //(16 times)
}
counterSet // {2, 4, 6, 5, 0, 1, 3}
array3 // ["a", "b", "c", "d", "e", "f", "g"]
I would still like some input. Thanks Kurt

Do you need shuffled index? If so, the code below does the thing.
( 0 ..< array2.count ).shuffled()
or
Array( stride( from: 0, through: array2.count - 1, by: 1 ) ).shuffled()

var array1 = ["A","B","C","D","E","F","G"]
var array2 = ["a","b","c","d","e","f","g"]
let randomIndex: Int = Int(arc4random()) % (array2.count)
print("Number:\(randomIndex)")
array2.insert(array1[randomIndex], at: randomIndex)
print("array2:\(array2)")

Related

Swift, how to use an array with indices to reorder another array

In Swift I have an array of indices that I want to use to permute an array of values (very easy to do in Matlab), but can't figure out a simple way of doing it (using a for-loop would be easy, but I'm looking for a more efficient method).
For example:
var indices = [1,0,2]
var values = ["A","B","C"]
var permute = values[indices]
// Hoping to print: permute = ["B","A","C"]
var indices = [1,0,2]
var values = ["A","B","C"]
var permute = indices.map({values[$0]})
print(permute)
An alternative solution using compactMap,
let indices = [1, 0, 2]
var values = ["A", "B", "C"]
let permute = indices.compactMap({ values[$0] })
print(permute)
let indices = [1,0,2]
let values = ["A","B","C"]
var result: [String] = []
indices.forEach({ result.append(values[$0]) })
print(result) //["B", "A", "C"]
You can just map the keys array to the values array.
var keys = [1,0,2]
var values = ["a","b","c"]
print(keys.map({values[$0]}))

Swift Comparing Elements of Two Separate Arrays

I am trying to compare the elements in two different arrays in Swift without using higher order functions. The function should return an array of integers that are in both arrays. I think I am close, but am getting 'an index out range error. Also would like to know how this measures on time complexity
let arrayOne = [1, 5, 12, 3, -15 , 52, 20]
let arrayTwo = [3, 1, 6, 5, 57, 13, 17, 20]
func compareElementsInArray(array1:[Int], array2: [Int]) -> [Int] {
let totalArray = array1 + array2
var sortedArray = totalArray.sorted()
var results = [Int]()
for i in totalArray {
if sortedArray[i + 1] == sortedArray[i] {
results.append(sortedArray[i])
}
}
return results
}
compareElementsInArray(array1: arrayOne, array2: arrayTwo)
The problem is that you are iterating through all element of totalArray meaning that i will reach the last index of totalArray, then you are trying to access the i+1-th element of sortedArray, which has the same length as totalArray, hence the error.
You need to stop the loop at the index before the last one, not the last one.
func compareElementsInArray(array1:[Int], array2: [Int]) -> [Int] {
let totalArray = array1 + array2
var sortedArray = totalArray.sorted()
var results = [Int]()
for i in 0..<totalArray.count-1 {
if sortedArray[i + 1] == sortedArray[i] {
results.append(sortedArray[i])
}
}
return results
}
print(compareElementsInArray(array1: arrayOne, array2: arrayTwo))
However, you can use an NSCountedSet to achieve the same using higher order functions (your solutions doesn't actually use higher order functions).
You just have to create a counted set from the combination of the arrays, then use flatMap to filter the elements whose count is greater than 1 and map the result to [Int].
func nonUniqueElements(array1: [Int], array2: [Int])->[Int] {
let countedSet = NSCountedSet(array: array1+array2)
return countedSet.flatMap({ element in
if countedSet.count(for: element) > 1 {
return element as? Int
} else {
return nil
}
})
}
nonUniqueElements(array1: arrayOne, array2: arrayTwo)

Swift: How do i insert item to array with specific order?

I want to insert item at specific index in Swift. But an error has occurred: Index is out of range
var a = [String]()
a.insert("a", atIndex: 3)
a.insert("b", atIndex: 1)
a.insert("c", atIndex: 0)
a.insert("d", atIndex: 2)
print(a)
If i have to use append or insert sequentially, it will break the order i wanted.
The answer by #fiks does explain the problem and a the correct solution if you want to use Array.
Dictionary
Since you want to perform random insertions, using a Dictionary instead of Array does seem a more flexible choice.
var dict: [Int:String] = [:]
dict[3] = "a"
dict[1] = "b"
dict[0] = "c"
dict[2] = "d"
print(dict) // [2: "d", 3: "a", 1: "b", 0: "c"]
print(dict.sort { $0.0.0 < $0.1.0}.map { $0.1 } ) // ["c", "b", "d", "a"]
You are trying to insert an item in the empty array at index 3. This causes the crash.
You may want to initialise the array with default values:
var a = Array<String>(count: 10, repeatedValue: "")
a.insert("a", atIndex: 3)
print(a)
But then you are not inserting, you are jus modifying the value, so you could achieve the same in this way:
var a = Array<String>(count: 10, repeatedValue: "")
a[3] = "a"
print(a)
The problem with your code is that the second parameter of insert must be less than or equal to the array's endIndex.
When you want to insert an element at index 3 to an array that has zero length, you can't! Because if there is an element at index 3, there must be an element at index 0, 1, 2 as well! That's how arrays work: it's sequential.
I guess you want the elements at index 0, 1 and 2 to be some default value. Then use this extension
extension Array {
mutating func insert(element: Element, atIndex index: Int, withDefaultValue value: Element) {
if self.endIndex < index {
let diff = index - endIndex
for _ in 0...diff {
self.append(value)
}
self[endIndex - 1] = element
} else {
insert(element, atIndex: index)
}
}
}
It's pretty much self-explanatory.
This can also be an XY problem. You might just need a [Int: String] for this. But without further info, I don't know.

How do I fetch the i'th element from a Swift ArraySlice?

Below I am trying to fetch the i'th element of the ArraySlice draggignFan. The code builds fine (no warnings) but the program dies at runtime on the line where I try to index the slice like a normal array:
var draggingFan : ArraySlice<Card>?
...
if let draggingFan = draggingFan {
for i in 1 ..< draggingFan.count {
let card = draggingFan[i] // EXECUTION ERROR HERE
...
}
}
According to the docs there is a first and last method (which I use elsewhere with no problem). So how do I index an ArraySlice in Swift? (Note: I am intentionally skipping the 0'th index in the slice -- that's needed elsewhere).
The indices of the ArraySlice still match those of the original array. In your case, you are accessing index 1 which is not in your slice. If you offset the index by draggingFan.startIndex it will work:
if let draggingFan = draggingFan {
for i in 1 ..< draggingFan.count {
let card = draggingFan[draggingFan.startIndex + i]
...
}
}
Alternatively:
if let draggingFan = draggingFan {
for i in draggingFan.startIndex + 1 ..< draggingFan.endIndex {
let card = draggingFan[i]
...
}
}
This will access the values from the second element in the slice to the last element in the slice:
let original = [1,2,3,4,5,6] // Int array to demonstrate
var draggingFan : ArraySlice<Int>?
draggingFan = original[1...4] // create the slice
if let draggingFan = draggingFan {
// so there's no errors just slice the slice and iterate over it
for i in draggingFan[(draggingFan.startIndex+1)..<draggingFan.endIndex] {
print(i, terminator: ", ")
}
}
Output:
3, 4, 5,
The reason you are having this problem is that the slice maintains the original index numbers of the sequence you got it from. Thus, element 1 is not in this slice.
For example, consider this code:
let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]
Now what is slice[1]? It isn't 4, even though that is the second thing in the slice. It's 2, because the slice still points into the original array. In other words, slice[1] is out of the slice's range! That is why you're getting a runtime error.
What to do? Well, the actual indexes of the slice are its indices. That is what you want to cycle thru. But... You don't want the first element pointed to by the slice. So you need to advance the startIndex of the range you're going to iterate through. Thus:
if let draggingFan = draggingFan {
var ixs = draggingFan.indices
ixs.startIndex = ixs.startIndex.advancedBy(1)
for i in ixs {
// ... now your code will work ...
}
}
However, in my view, there's no need to index the slice at all, and you shouldn't be doing so. You should cycle through the slice itself, not thru its indexes. You have this:
for i in 1 ..< draggingFan.count
But that is much like saying
for aCard in draggingFan
...except that you want to drop the first element of the slice. Then drop it! Say this:
for aCard in draggingFan.dropFirst()
To see that this will work, try this in a playground:
let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]
for anInt in slice.dropFirst() {
print(anInt) // 4, 5, 6
}
As you can see, we are cycling through exactly the desired elements, with no reference to index numbers at all.
To iterate over the elements in the slice:
draggingFan?.forEach({ (element)
...
})
As far as I know, the get a specific element, it needs to be converted back to an array e.g.
let draggingFanArray = Array(draggingFan!)
Here's the playground code I used to toy around with various scenarios:
import Cocoa
var a: Array<Int>?
var b: ArraySlice<Int>?
a = [1, 2, 3, 4, 5, 6, 7]
b = a![3...5]
let count = b!.count
b!.forEach({ (element) in
print("\(element)")
})
let c = Array(b!)
print(c[2])
edit ArraySlice extension though:
extension ArraySlice {
func elementAtIndex(index: Int)->AnyObject?{
return Array(self)[index] as? AnyObject
}
}
If I have an array:
var arr = [1, 2, 3, 4, 5, 6, 7] // [1, 2, 3, 4, 5, 6, 7]
And I take a slice of the array:
let slice = arr[3..<arr.count] // [4, 5, 6, 7]
This slice will have a startIndex of 3, which means that indexing starts at 3 and ends at 6.
Now if I want a slice containing everything but the first element, I can use the dropFirst() method:
let sliceMinusFirst = slice.dropFirst() // [5, 6, 7]
And at this point, sliceMinusFirst has a startIndex of 4, which means my indexes range from 4 to 6.
Now if I wish to iterate over these to do something with the items, I can do the following:
for item in sliceMinusFirst {
print(item)
}
Alternatively, I can do it with forEach:
sliceMinusFirst.forEach { item in
print(item)
}
By using these forms of iteration, the fact that the startIndex is nonzero doesn't even matter, because I don't use the indices directly. And it also doesn't matter that, after taking a slice, I wanted to drop the first item. I was able to do that easily. I could have even done that at the time I wanted to do the iteration:
slice.dropFirst().forEach { item in
print(item)
}
Here I dropped the first item from the original slice, without creating any intermediate variables.
Remember that if you need to actually use the index, you're probably doing something wrong. And if you genuinely do need the index, make sure you understand what's going on.
Also if you want to get back to zero-based indexing once you make a slice, you can create an array from your slice:
let sliceArray = Array(slice) // [4, 5, 6, 7]
sliceArray.startIndex // 0

Populate Array with a set of Strings from a for-in loop for Swift

I am kinda stumped on figuring this out. I want to populate an array with the string values that comes from a for-in loop.
Here's an example.
let names = ["Anna", "Alex", "Brian", "Jack"]
for x in names {
println(x)
}
The current x value would generate 4 string values (Anna, Alex, Brian, Jack).
However I need some advice in going about getting these four values back into an array. Thank you in advance.
Whatever is on the right side of a for - in expression must be a SequenceType. Array, as it happens, can be initialised with any SequenceType. So if you're just doing something like this:
var newArray: [String] = []
for value in exoticSequence {
newArray.append(value)
}
The same thing can be accomplished (faster), by doing this:
let newArray = Array(exoticSequence)
And it doesn't matter what type exoticSequence is: if the for-in loop worked, Array() will work.
However, if you're applying some kind of transformation to your exoticSequence, or you need some kind of side effect, .map() might be the way to go. .map() over any SequenceType can return an array. Again, this is faster, and more clear:
let exoticSequence = [1, 2, 3]
let newArray = exoticSequence.map {
value -> Int in
// You can put whatever would have been in your for-in loop here
print(value)
// In a way, the return statement will replace the append function
let whatYouWouldHaveAppended = value * 2
return whatYouWouldHaveAppended
}
newArray // [2, 4, 6]
And it's equivalent to:
let exoticSequence = [1, 2, 3]
var newArray: [Int] = []
for value in exoticSequence {
print(value)
let whatYouWouldHaveAppended = value * 2
newArray.append(whatYouWouldHaveAppended)
}
newArray // [2, 4, 6]

Resources