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.
Related
I want to make a Array of Arrays for Strings [[String]]() and be able to specify by index which inner list i want to append to
I have a var declared as:
var myList = [[String]]
I ideally want it to be something like this : [ ["a","b"], ["l","m"] ] and have the ability to specify which inner list gets the new element/ letter via a index
I have tried :
myList[index].insert("c", at: index) //gives and error saying index out of range
myList[index].append("c") //gives and error saying index out of range
if myList[index].isEmpty {
myList[index].append("c")
}//gives and error saying index out of range
but whenever i try to add to it i get an index out of range error as shown above.
The issue you are running into is that since you initialize myList as an empty array (var myList: [[String]] = []), you cannot reference items at specific indexes.
You can make a method like this to make sure you aren't accessing non-existant indexes:
func addToNested(_ index: Int, _ item: String) {
while index >= myList.count {
myList.append([])
}
myList[index].append(item)
}
This way it will check to see that you have enough arrays nested inside myList. That way if you call the following:
addToNested(0, "a")
addToNested(1, "l")
addToNested(0, "b")
addToNested(1, "m")
myList will be equal to [["a", "b"], ["l", "m"]]
You need to specify the size of the first dimension first. If you know exactly that you're gonna have n arrays, you can fix the problem by:
var myList: [[String]] = .init(repeating: [], count: n)
// which is shorthand for
var myList: [[String]] = Array(repeating: [], count: n)
// which can be rewritten like
var myList = Array<[[String]]>(repeating: [], count: n)
And you can use it like:
var myList: [[String]] = .init(repeating: [], count: 4)
myList[0].append("a")
myList[1].append("b")
myList[1].append("c")
myList[2].append(contentsOf: ["d", "f"])
myList[2].insert("e", at: 1)
print(myList)
> [["a"], ["b", "c"], ["d", "e", "f"], []]
Or if you know that it's going to be NxM matrix, you can also do
var myList: [[String]] = .init(repeating: .init(repeating: "", count: m), count: n)
I have to filter an array having the most occurrences of element.
Initial Array :
let array1 = [1,2,3,2,4,2,5,3]
let array2 = ["abc", "def", "abc", "ert", "def", "abc"]
After Filtering, Final Array :
let filteredArray1 = [2,2,2]
let filteredArray2 = ["abc","abc","abc"]
I got the idea to get the count of elements from here:
How to count occurrences of an element in a Swift array?
Like getting the count of "abc" :
array2.filter{$0 == "abc"}.count
But is there any way to get the filtered array ?
You can group the items into a dictionary and compare the number of items in each group
let mostFrequent = Dictionary(grouping: array1, by: {$0})
.max(by: {$0.value.count < $1.value.count})?.value ?? []
The issue with the above is that if there are two or more values with the same count only one will be selected.
The below solution handles when there are multiple max counts, I couldn't write it as a single line expression though
let dictionary = Dictionary(grouping: array1, by: {$0})
let max = dictionary.max(by: {$0.value.count < $1.value.count})?.value.count ?? 0
let mostFrequent = dictionary.filter { $0.value.count == max }.values
Using NSCountedSet 🔢
You can define this extension for the Array type
extension Array where Element: Equatable {
func filteredByMostPopular() -> [Element] {
let countedSet = NSCountedSet(array: self)
let mostPopularElement = self.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
return self.filter { $0 == mostPopularElement }
}
}
How does it work?
The extension uses NSCountedSet to find the "most popular" element.
If 2 or more elements are the most popular the first one is choosen.
Then the array is filtered using the most popular element.
Test
array1.filteredByMostPopular() // [2, 2, 2]
array2.filteredByMostPopular() // ["abc", "abc", "abc"]
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)")
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)
I am currently trying to make an app for iOS but I can't get some simple code down. Basically I need to randomly select 5 elements from an array list without repeating an element. I have a rough draft, but it only displays one element.
Here is my code:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
let randomIndex1 = Int(arc4random_uniform(UInt32(array1.count)))
print(array1[randomIndex1])
You can do it like this:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza", "curry", "sushi", "pizza"]
var resultSet = Set<String>()
while resultSet.count < 5 {
let randomIndex = Int(arc4random_uniform(UInt32(array1.count)))
resultSet.insert(array1[randomIndex])
}
let resultArray = Array(resultSet)
print(resultArray)
A set can contain unique elements only, so it can't have the same element more than once.
I created an empty set, then as long as the array contains less than 5 elements (the number you chose), I iterated and added a random element to the set.
In the last step, we need to convert the set to an array to get the array that you want.
SWIFT 5.1
This has been answered, but I dare to come with a more simple solution.
If you take your array and convert it into a Set you will remove any duplicated items and end up with a set of unique items in no particular order since the nature of a Set is unordered.
https://developer.apple.com/documentation/swift/set
If you then convert it back to an array and pick 5 elements you will end up with an array of random items.
But it's just 1 line ;)
Example:
var original = ["A","B","C","C","C","D","E","F","G","H"]
let random = Array(Set(original)).prefix(5)
Example print:
["B", "G", "H", "E", "C"]
The only requirement is your objects must conform to the Hashable protocol. Most standard Swift types do, otherwise, it's often simple to make your own types conform.
https://developer.apple.com/documentation/swift/hashable
If you don't care about changing the original array, the following code will put the picked elements at the end of the array and then it will return the last part of the array as a slice.
This is useful if you don't care about changing the original array, the advantage is that it doesn't use extra memory, and you can call it several times on the same array.
extension Array {
mutating func takeRandomly(numberOfElements n: Int) -> ArraySlice<Element> {
assert(n <= self.count)
for i in stride(from: self.count - 1, to: self.count - n - 1, by: -1) {
let randomIndex = Int(arc4random_uniform(UInt32(i + 1)))
self.swapAt(i, randomIndex)
}
return self.suffix(n)
}
}
Example:
var array = [1,2,3,4]
let a1 = array.takeRandomly(numberOfElements: 2)
let a2 = array.takeRandomly(numberOfElements: 2)
swift-algorithms now includes an extension to Sequence called randomSample.
import Algorithm
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
array1.randomSample(count: 5)
Just my ¢2:
Moe Abdul-Hameed's solution has one theoretical drawback: if you roll the same randomIndex in every iteration, the while loop will never exit. It's very unlike tho.
Another approach is to create mutable copy of original array and then exclude picked items:
var source = array1
var dest = [String]()
for _ in 1...5 {
let randomIndex = Int(arc4random_uniform(UInt32(source.count)))
dest.append(source[randomIndex])
source.remove(at: randomIndex)
}
print(dest)
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
while array1.count > 0 {
// random key from array
let arrayKey = Int(arc4random_uniform(UInt32(array1.count)))
// your random number
let randNum = array1[arrayKey]
// make sure the number ins't repeated
array1.removeAtIndex(arrayKey)
}
By removing your picked value from array, prevent's from duplicates to be picked