Swift define datatype of array - arrays

I need to somehow cast a 2dimensional array...
console:
solution.swift:22:23: error: missing argument label 'arr:' in call
array = invertArr(arr)
^
arr:
solution.swift:53:12: error: cannot convert return expression of type '[[Any]]' to return type '[[Int]]'
return result
^~~~~~
as! [[Int]]
and that's the code:
func invertArr(arr:[[Int]]) -> [[Int]]{
var counter = 0
var result = [[]]
for element in arr{
if counter == 0{
continue
}
var counter2 = 0
for item in element.reversed(){
result[counter][counter2] = item
counter2 += 1
}
counter += 1
}
return result
}
Thank you for helping!

Because of two serious issues your code cannot work anyway even if you call the method with the arr parameter label and declare result as [[Int]]().
As counter is 0 the first loop continues always and an empty array is returned.
result[counter][counter2] = item crashes reliably because there are no items at the given indices.
To invert the order of the items in the inner arrays this is a generic version
func invertArr<T>(arr: [[T]]) -> [[T]]{
var result = arr
for (index, element) in arr.enumerated() {
result[index] = element.reversed()
}
return result
}
let array = [[1, 2, 3, 4], [5, 6, 7, 8]]
let inverted = invertArr(arr: array) // [[4, 3, 2, 1], [8, 7, 6, 5]]
If you want to reverse also the items in the outer array return result.reversed()

Related

Swift : Performing operations on certain elements of an array

So, something is bugging me with the syntax in Swift for performing operations on Arrays of Ints.
What I wanna do is this : I have an array of Ints which is outputted from a function, its size (count) varies between say 2 and 6 for now, depending on buttons I press in my app.
For each array that is outputted and that contain n ints, I want to create n arrays on which to perform an other action later on.
These "sub" arrays are supposed to be calculated this way :
newArray1's values should be array's values - the value of the first index of newArray1
newArray2's values should be array's values - the value of the second index of newArray2
etc... (I'll automate the number of newArrays according to the array.count)
An other condition applying for those new arrays is that if at a given index the value is negative, I add 12 (so it'll occur for newArray2 at index 1, for newArray3 at indexes 1 & 2, etc... as long as those newArrays are created).
Here's how I wanted to perform that (I created this with dummy arbitrary array in the playground for the sake of testing before inserting the correct stuff in my app code) :
var array : [Int] = [2,4,6,8,9]
var newArray2 = [Int]()
var increment2 = Int()
increment2 = array[1]
newArray2 = array.map {$0 - increment2}
for i in 0..<newArray2.count {
if array[i] < 0 {
newArray2[i] = array[i] + 12
} else {
newArray2[i] = array[i]
}
}
print(array)
print(newArray2)
So of course it doesn't work because I can't seem to figure how to correctly perform operations on Arrays...
Intuitively it seems in my first if statement I'm comparing not the element at index i but i itself, not sure how to reformat that though...
Any help is most welcome, thanks in advance ! :)
[EDIT: I just edited the names of newArray1 to newArray2, same for increments, so that I have negative values and it matches the index value of 1 which is the second element of my main array]
You seem to mean this:
let arr = [2,4,6,8,9]
var results = [[Int]]()
for i in arr.indices {
results.append(arr.map {
var diff = $0-arr[i]
if diff < 0 { diff += 12 }
return diff
})
}
// results is now:
// [[0, 2, 4, 6, 7],
// [10, 0, 2, 4, 5],
// [8, 10, 0, 2, 3],
// [6, 8, 10, 0, 1],
// [5, 7, 9, 11, 0]]

Swift remove objects in Array range

I have an array as a property in a class.
Class Custom {
let objArray: [CustomClass]
}
I want to remove some items in objArray in a range. So I have done below
let newVar = objArray[1...3]
new objects are correctly removed but return value is in newVar since array is value type how I can make the original reflect the same.
Below code gets Index out of bounds as the indexes incremented
for i in 1...3 {
objArray.remove(at: 1)
}
======
What is the best approach for the above issue.
Any hint in right direction would be highly appreciated.
Use removeSubrange method of array. Make a valid range by element location and length.
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let range = 1...3
array.removeSubrange(range)
print(array)
Output: [1, 5, 6, 7, 8, 9, 10]
Note: Range should be a valid range I mean it should not be out from array.
Here is yours way (by for loop)
We can not remove objects by their indexes in a loop because every time object removes array's count and objects indexes will be change so out of range crash can come or you might get a wrong output. So you will have to take help of another array. See below example:-
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
let minRange = 1
let maxRange = 3
for i in 0..<array.count {
if i >= minRange && i <= maxRange {
/// Avoid
continue
}
newArray.append(array[i])
}
print(newArray)
Output: [1, 5, 6, 7, 8, 9, 10]
If you want to remove items by index in a range you have to inverse the indexes to start with the highest index otherwise you will get the out-of-range exception. Consider also that indexes are zero-based.
That's a safe version which checks also the upper bound of the array.
var array = [1, 2, 3, 4, 5, 6]
for i in (0...3).reversed() where i < array.count {
array.remove(at: i)
}
print(array) // [5, 6]
You can find a more generic and more efficient solution here
This solution also returns the removed values
extension Array {
/**
* ## Examples:
* var arr = [0,1,2,3]
* arr.remove((0..<2)) // 0,1
* arr // 2,3
*/
mutating func remove(_ range: Range<Int>) -> Array {
let values = Array(self[range])
self.removeSubrange(range)
return values
}
}
The issue you are having is that an array index is zero based, which is to say, the first element in an array is accessed bv:
Let firstArrayValue = objArray[0]
So in the case of your for loop, you need to subtact 1 from i to get the proper index value:
for i in 1…3 {
objArray.remove(at: i-1)
}
A better way is to loop through the indices by starting at 0. i = 0 will reference the first value in your objArray:
for i in 0...2 {
objArray.remove(at: i)
}
If you need to remove elements in the middle of the array you must first find their index location then remove. To find the index:
let indexLocation = objArray(indexOf: "Value in Array")
Then remove:
objArray.remove(at: indexLocation)

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]

Find min / max value in Swift Array

Given an array of Swift numeric values, how can I find the minimum and maximum values?
I've so far got a simple (but potentially expensive) way:
var myMax = sort(myArray,>)[0]
And how I was taught to do it at school:
var myMax = 0
for i in 0..myArray.count {
if (myArray[i] > myMax){myMax = myArray[i]}
}
Is there a better way to get the minimum or maximum value from an integer Array in Swift? Ideally something that's one line such as Ruby's .min and .max.
Given:
let numbers = [1, 2, 3, 4, 5]
Swift 3:
numbers.min() // equals 1
numbers.max() // equals 5
Swift 2:
numbers.minElement() // equals 1
numbers.maxElement() // equals 5
To calculate an array's min and max values yourself, you can use reduce. This was a key solution prior to .min() and .max() appearing in Swift.
Use the almighty reduce:
let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, { max($0, $1) })
Similarly:
let numMin = nums.reduce(Int.max, { min($0, $1) })
reduce takes a first value that is the initial value for an internal accumulator variable, then applies the passed function (here, it's anonymous) to the accumulator and each element of the array successively, and stores the new value in the accumulator. The last accumulator value is then returned.
With Swift 5, Array, like other Sequence Protocol conforming objects (Dictionary, Set, etc), has two methods called max() and max(by:) that return the maximum element in the sequence or nil if the sequence is empty.
#1. Using Array's max() method
If the element type inside your sequence conforms to Comparable protocol (may it be String, Float, Character or one of your custom class or struct), you will be able to use max() that has the following declaration:
#warn_unqualified_access func max() -> Element?
Returns the maximum element in the sequence.
The following Playground codes show to use max():
let intMax = [12, 15, 6].max()
let stringMax = ["bike", "car", "boat"].max()
print(String(describing: intMax)) // prints: Optional(15)
print(String(describing: stringMax)) // prints: Optional("car")
class Route: Comparable, CustomStringConvertible {
let distance: Int
var description: String { return "Route with distance: \(distance)" }
init(distance: Int) {
self.distance = distance
}
static func ==(lhs: Route, rhs: Route) -> Bool {
return lhs.distance == rhs.distance
}
static func <(lhs: Route, rhs: Route) -> Bool {
return lhs.distance < rhs.distance
}
}
let routes = [
Route(distance: 20),
Route(distance: 30),
Route(distance: 10)
]
let maxRoute = routes.max()
print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)
#2. Using Array's max(by:) method
If the element type inside your sequence does not conform to Comparable protocol, you will have to use max(by:) that has the following declaration:
#warn_unqualified_access func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
Returns the maximum element in the sequence, using the given predicate as the comparison between elements.
The following Playground codes show to use max(by:):
let dictionary = ["Boat" : 15, "Car" : 20, "Bike" : 40]
let keyMaxElement = dictionary.max(by: { (a, b) -> Bool in
return a.key < b.key
})
let valueMaxElement = dictionary.max(by: { (a, b) -> Bool in
return a.value < b.value
})
print(String(describing: keyMaxElement)) // prints: Optional(("Car", 20))
print(String(describing: valueMaxElement)) // prints: Optional(("Bike", 40))
class Route: CustomStringConvertible {
let distance: Int
var description: String { return "Route with distance: \(distance)" }
init(distance: Int) {
self.distance = distance
}
}
let routes = [
Route(distance: 20),
Route(distance: 30),
Route(distance: 10)
]
let maxRoute = routes.max(by: { (a, b) -> Bool in
return a.distance < b.distance
})
print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)
The other answers are all correct, but don't forget you could also use collection operators, as follows:
var list = [1, 2, 3, 4]
var max: Int = (list as AnyObject).valueForKeyPath("#max.self") as Int
you can also find the average in the same way:
var avg: Double = (list as AnyObject).valueForKeyPath("#avg.self") as Double
This syntax might be less clear than some of the other solutions, but it's interesting to see that -valueForKeyPath: can still be used :)
You can use with reduce:
let randomNumbers = [4, 7, 1, 9, 6, 5, 6, 9]
let maxNumber = randomNumbers.reduce(randomNumbers[0]) { $0 > $1 ? $0 : $1 } //result is 9
Swift 3.0
You can try this code programmatically.
func getSmallAndGreatestNumber() -> Void {
let numbers = [145, 206, 116, 809, 540, 176]
var i = 0
var largest = numbers[0]
var small = numbers[0]
while i < numbers.count{
if (numbers[i] > largest) {
largest = numbers[i]
}
if (numbers[i] < small) {
small = numbers[i]
}
i = i + 1
}
print("Maximum Number ====================\(largest)")// 809
print("Minimum Number ====================\(small)")// 116
}
With Swift 1.2 (and maybe earlier) you now need to use:
let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, combine: { max($0, $1) })
For working with Double values I used something like this:
let nums = [1.3, 6.2, 3.6, 9.7, 4.9, 6.3];
let numMax = nums.reduce(-Double.infinity, combine: { max($0, $1) })
In Swift 2.0, the minElement and maxElement become methods of SequenceType protocol, you should call them like:
let a = [1, 2, 3]
print(a.maxElement()) //3
print(a.minElement()) //1
Using maxElement as a function like maxElement(a) is unavailable now.
The syntax of Swift is in flux, so I can just confirm this in Xcode version7 beta6.
It may be modified in the future, so I suggest that you'd better check the doc before you use these methods.
Here's a performance test for the solutions posted here. https://github.com/tedgonzalez/MaxElementInCollectionPerformance
This is the fastest for Swift 5
array.max()
var numbers = [1, 2, 7, 5];
var val = sort(numbers){$0 > $1}[0];
Apple's Swift Algorithms introduced 2021 contains Minima and/or Maxima which is likely highly optimized.
Example from the documentation:
let numbers = [7, 1, 6, 2, 8, 3, 9]
if let (smallest, largest) = numbers.minAndMax(by: <) {
// Work with 1 and 9....
}
The total complexity is O(k log k + nk), which will result in a runtime close to O(n) if k is a small amount. If k is a large amount (more than 10% of the collection), we fall back to sorting the entire array. Realistically, this means the worst case is actually O(n log n).
Updated for Swift 3/4:
Use below simple lines of code to find the max from array;
var num = [11, 2, 7, 5, 21]
var result = num.sorted(){
$0 > $1
}
print("max from result: \(result[0])") // 21
Just curious why you think the way it was taught in school is potentially expensive? You're running a for loop which has the time complexity of O(N). That's actually better than most sorting algorithms and equivalent to the performance of higher-order methods like reduce.
So I would think that in terms of performance, a for loop is as good as it gets. I don't think you'll find anything better than O(N) for finding max.
Of course, just use the .max() method provided by apple is the way to go.
If both minimum and maximum values are desired, an efficient approach could be to use a single reduce operation with a tuple:
let values = [11, 2, 7, 5, 21]
let (minimum, maximum) = values.reduce((Int.max, Int.min)) {
(min($0.0, $1), max($0.1, $1))
}
print(minimum, maximum) // 2, 21
let array: [Int] = [2, -22, -1, -5600, 333, -167]
var min = 0
var max = array[0]
for i in array {
// finding the min
if min > i {
min = i
}
// finding the max
if max < i {
max = i
}
}
print("Minimum: \(min)\nMaximum: \(max)")
You can also sort your array and then use array.first or array.last

Resources