Swift Index and it's implementation - swift

I'm new to programming. I was trying to understand how indices work in swift. This is the following code from swift documents.
converted into function.
func ind(){
var c = [10, 20, 30, 40, 50] //index = [0:5]
var i = c.startIndex //i = [0]
while i != c.endIndex { // i!= [5] , while i not equal to [5]
c[i] /= 5
i = c.index(after: i)
}
print(c) // [2,4,6,8,10]
}
the code line i = c.index(after: i) doesn't seems to make sense to me. "after" means the character of string after the string.index, but because we initialized the 'i' to be zero(0) the output should stay [4 and onwards]. secondly, if i replace the i let's say with integer 2. the loop keeps repeating itself. why? Thank you in advance for your time

after means the next element on your list in this context.
An index is more general and is not limited to String.
In addition, an index can have different types depending on the structure that you are manipulating.
For instance:
var c1 = [10, 20, 30, 40, 50] //
var i1 = c1.startIndex
// Print: Int
print(type(of: i1))
var c2 = "Hello" //
var i2 = c2.startIndex
// Print: Index
print(type(of: i2))
You can even create your own index for a specific type, as long as you provide a way to compute the next index.
Thus, in your code for this particular example, the index is of type Int.
If you change the value of i to be the constant 2, you can never equal the value of the end index (which is 5 here).
Then, your loop will never end.

Related

How to create a new array of even numbers from an existing array?

I am trying to create a function that takes an array of Int, and returns a new array of all of the even numbers in the original array.
I have been fumbling around with this code (I am a very new beginner)
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
func newInt(newEven: Int, newArray: [Int]) -> Int {
for newEven in numberArray{
var index = 0
index += 1
if newEven % 2 == 0 {
newArray.insert(newEven, at:[index])
}
return newEven
}
}
print(newArray)
This is a good start! Here are some pointers:
1. Formatting
The formatting needs some work. Generally, every new scope ({ ... }) should introduce a new layer of indentation, like so:
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
func newInt(newEven: Int, newArray: [Int]) -> Int {
for newEven in numberArray{
var index = 0
index += 1
if newEven % 2 == 0 {
newArray.insert(newEven, at:[index])
}
return newEven
}
}
print(newArray)
Now we can make some observations:
1. index is scoped to the for loop body, and will always have the same value of 0, and then 1 after the next line.
2. The return statement is within the for loop body, and unconditional. This function will always return the value of the first element of numberArray
3. The return type of this function is Int. But in your question, you state that you want this to return an array of all of the even numbers. So your return type will have to be Array<Int> (a.k.a. [Int]), not just Int.
2. Compilation issues
This function has several errors that will prevent compilation:
The return statement is within a loop body. If numberArray is empty, and the for loop body is never entered, then you don't hit the return statement. Once control reaches the end of the function, what value should be returned? It's not defined, so that's an error. In Swift, all code paths through a function must return a value. (with the exception of Void functions, which implicitly return nothing at the end)
You're trying to call Array.insert(_:at:) with a second argument of [index], which is an array literal of type Array<Int>. It should just be index.
3. Logical issues
Your function introduces a parameter called newArray, which shadows the global variable newArray on the line before it. The global variable isn't necessary, and you should delete it.
Your function operates over numberArray, but doesn't explicitly take it as input via a parameter. Rather than hardcoding a reference to a global variable like numberArray, you should use a parameter.
The parameter newEven is unused, and is shadowed by the local variable of the for loop
Your function name newInt(newEven:newArray:) doesn't describe what the function does. Consider a function signature like func allEvens(in input: [Int]) -> [Int]
You never actually call this function. You declare it, but never told the program to run it.
You don't need to use Array.insert(_:at:). You can simply use Array.append, which will automatically append elements to the end of the array.
4. Recommendations
Fix the method signature. You want the function to take some numbers, and output only the even numbers. Model that in code: func allEvens(in input: [Int]) -> [Int]
Create a new empty array locally (within the function), into which the even numbers will be stored. As you loop over the input array, check every number if it's even, and if so, append it to the evens array.
Return the evens array.
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray = numberArray.filter({$0 % 2 == 0})
This should return a new array with even numbers.
As LeoDabus mentioned in his comment, the functionality you're seeking is already contained within Swift's Standard Library, so it's not really necessary to write a dedicated function to accomplish that task. Here's how you would do it:
let numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let newArray = numberArray.filter { $0.isMultiple(of: 2) }
print(newArray) // [2, 4, 6, 8, 10]
In this, you're using the filter whether $0 (where $0 is an element in your array) is a multiple of the number you specified in the parameter, in this case, 2.
As you see in the documentation, isMultiple(of:) returns a Bool (true or false). This is the signature:
func isMultiple(of other: Self) -> Bool
I would recommend having a peek at this post covering the topics of map, filter, and reduce. These are useful things to know when starting out in Swift.
Additionally, I've found Dash to be extremely helpful in navigating documetation for Swift.
Update
I should have read your question more thoroughly, as I missed the part where you've gotta do it with a loop. Swift has a cool method called forEach, which I'm a huge fan of. Using that methodology, it would look something like this:
func filter(array: [Int], forMultiplesOf multiple: Int) -> [Int] {
// Create a landing spot for whatever is a multiple...it starts as empty
var newArray: [Int] = []
// This is not the most compact way, but it satisfies the loop constraint
array.forEach { number in
if number % multiple == 0 {
newArray.append(number)
}
}
// Once you're done with the loop, then return the new array you declared at the beginning
return newArray
}
And you'd call it like so:
let newArrayUsingFunction = filter(array: numberArray, forMultiplesOf: 2)
What you're doing here is passing in 2 parameters to the function (array & multiple) to return an array of Ints. See comments in code for what's going on

how do I get the index from this function

this is demo of iOS Charts library (LineChart) and I want to input my data instead of arc4random data.
My data is in Array so I have to approach with index but I can't understand the (0..<count).map { (i) -> ChartDataEntry code.
func setChartValues(_ count : Int = 24) {
let values = (0..<count).map { (i) -> ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
let set1 = LineChartDataSet(entries: values , label : "DataSet 1")
let data = LineChartData(dataSet: set1)
self.lineChartView.data = data
}
It seems you are new to iOS and swift. What you are looking for is an understanding of the functionning of closures in swift, plus the map function which is called an high order function
from apple doc ( https://developer.apple.com/documentation/swift/array/3017522-map ) :
Returns an array containing the results of mapping the given closure over the sequence’s elements.
In other words it maps your array into another array, according to the trailing closure you passed as a parameter.
In your specific case here his how to read it :
(0..<count) : creates an array of count lengh
example : if count = 4 then (0..<count) is [0, 1, 2, 3]
As said previously the map function will transform each of your element into another ( therefore keeping the length of the array ).
in your case val = Double(arc4random_uniform(UInt32(count))+3) will be equal to a random number calculated with count value, and create a new ChartDataEntry with this random value.
to sum it up the whole code is just saying "I will create a count length array of random ChartDataEntry", I guess as a mockup
I suggest you to read about closures here :
https://medium.com/the-andela-way/closures-in-swift-8aef8abc9474
and high order functions ( such as map(_:) ) here :
https://medium.com/#abhimuralidharan/higher-order-functions-in-swift-filter-map-reduce-flatmap-1837646a63e8
let values = (0.. ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
The value mapped and return is you can say a hash function. (arc4random).
It index you are taking is just setting X axis of the chart like 0 , 1 ,2 etc...
and your graph Y it set according to the functions return (arc4random)

Default dictionary value in Swift

I know I can have a default dictionary value in swift, but I am struggling to do this for a tuple
i.e.
var freq = [Int:(Int,Int,Int)]()
I want to make a default value
freq[num.element, default: (0, num.offset, num.offset) ] = (7, 0, 0 )
This produces the frequency table with 7,0,0 for every value when the key does not exist.
Can I use the default for a more complicated dictionary?
For an example if we have an array of numbers [1,2,2,3,3,3]
we can count the number of elements using a frequency table
var freq = [Int:Int]()
for num in nums {
freq[num, default: 0] += 1
}
We want to store the initial position of each number, and the final position of each number in the frequency table so use
var freq = [Int:(Int,Int,Int)]()
for num in nums.enumerated() {
if freq[num.element] == nil {
freq[num.element] = (1,num.offset, num.offset)
} else {
freq[num.element] = (freq[num.element]!.0 + 1, freq[num.element]!.1, num.offset)
}
}
For this code I want to use a default value.
I am looking for a way of using a default value on a frequency list containing more than just a single value. It is not relevant that I am using a tuple rather than an array of values, for an example I want to find out how to use default with a tuple using arrays.
I tried to use the example above to make a default value, and on testing it does not work. I have looked at previous questions, a Google Search and looked at Apple's documentation.
Question: How to use default for a dictionary var freq = Int:(Int,Int,Int)
Is this what you looking for? Just get the current value with default value and store it in a variable first makes life easier.
var freq = [Int:(Int,Int,Int)]()
var nums = [1,2,2,3,3,3]
for num in nums.enumerated()
{
let currentTuple = freq[num.element, default: (0,num.offset,num.offset)]
freq[num.element] = (currentTuple.0 + 1, currentTuple.1,num.offset)
}
print(freq) //output: [1: (1, 0, 0), 2: (2, 1, 2), 3: (3, 3, 5)]

Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant

I am currently struggling with an error for a homework assignment in my coding class. We are creating a loop that loops through an array of gpa values and then adds it to a variable named totalGradePoints. The problem is that I am coming across an error when the loop runs:
Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant
The error is on this line:
var totalGradePoints = Double()
for gpa in gpaValues {
let averageGPA: Double = gpa += totalGradePoints
}
Here is my full code:
//: Playground - noun: a place where people can play
import UIKit
// You are the university registrar processing a transfer student's transcripts that contains grades that are a mix of letters and numbers. You need to add them to our system, but first you need to convert the letters into grade points.
// Here's an array of the student's grades.
var transferGrades: [Any] = ["C", 95.2, 85, "D", "A", 93.23, "P", 90, 100]
// To prepare for converting the letters to numerical grades, create a function that returns a double, inside which you create a switch that will convert an A to a 95, B to 85, C to 75, D to 65, , P (for passing) to 75. Everything else will be a zero.
func gradeConverter(letterGrade: String) -> Double {
switch letterGrade {
case "A":
return 95
case "B":
return 85
case "C":
return 75
case "D":
return 65
case "P":
return 75
default: // Is this where everything else is zero?
return 0
}
}
// Create a new array called convertedGrades that stores doubles.
var convertedGrades: [Double] = [98.75, 75.5, 60.0, 100.0, 82.25, 87.5]
// Loop through the transferGrades array, inspecing each item for type and sending strings (your letter grades) to the function you just made and storing the returned double in your convertedGrades array. If your loop encounters a double, you can place it directly into the new array without converting it. It it encounters an int, you will need to convert it to a double before storing it. Print the array. (You may notice that some of your doulbes are stored with many zeros in the decimal places. It's not an error, so you can ignore that for now)
for grade in transferGrades {
if let gradeAsString = grade as? String {
gradeConverter(letterGrade: gradeAsString)
} else if let gradeAsDouble = grade as? Double {
transferGrades.append(gradeAsDouble)
} else if let gradeAsInt = grade as? Int {
Double(gradeAsInt)
transferGrades.append(gradeAsInt)
}
}
print(transferGrades)
// Now that we have an array of numerical grades, we need to calculate the student's GPA. Create a new array called GPAValues that stores doubles.
var gpaValues: [Double] = [2.5, 3.0, 4.0, 3.12, 2.97, 2.27]
// Like with the letter conversion function and switch you created before, make a new function called calculateGPA that takes a double and returns a double. Inside your function, create another switch that does the following conversion. Grades below 60 earn zero grade points, grades in the 60s earn 1, 70s earn 2, 80s earn 3, and 90s and above earn 4.
func calculateGPA(gpaValue: Double) -> Double {
switch gpaValue {
case 0..<59:
return 0
case 60...69:
return 1
case 70...79:
return 2
case 80...89:
return 3
case 90..<100:
return 4
default:
return 0
}
}
// Loop through your convertedGrades array and append the grade point value to the GPAValues array. Because your calculateGPA function returns a value, you can use it just like a varialbe, so rather than calculate the grade points and then put that varialbe in your append statement, append the actual function. i.e. myArray.append(myFunction(rawValueToBeConverted))
for gpa in gpaValues {
gpaValues.append(calculateGPA(gpaValue: gpa))
}
// Finally, calculate the average GPA by looping through the GPA and using the += operator to add it to a variable called totalGradePoints. You may need to initialize the variable before using it in the loop. i.e. var initialized = Double()
var totalGradePoints = Double()
for gpa in gpaValues {
let averageGPA: Double = gpa += totalGradePoints
}
// Count the number of elements in the array (by using the count method, not your fingers) and store that number in a variable called numberOfGrades. Pay attention to creating your variables with the right types. Swift will tell you if you're doing it wrong.
var numberOfGrades: Int = gpaValues.count
// Divide the totalGradePoints by numberOfGrades to store in a variable called transferGPA.
var transferGPA: Double = Double(totalGradePoints) / Double(numberOfGrades)
// Using code, add one numerical grade and one letter grade to the transferGrades array that we started with (i.e. append the values rather than manualy writing them into the line at the beginning of this file) and check that your transferGPA value updates. You'll need to append the new grades on the line below the existing transferGrades array so that your changes ripple through the playground.
transferGrades.append(97.56)
transferGrades.append("B")
averageGPA must be define using the var keyword to make it mutable later on when summing up the values.
var averageGPA: Double = 0
for gpa in gpaValues {
averageGPA += gpa
}
averageGPA = averageGPA / Double(gpaValues.count)
Recall the average is calculated by summing up the score and dividing the number of scores.
Defining something with let means that the following will be a constant.
let answer: Int = 42
answer = 43 /* Illegal operation. Cannot mutate a constant */
Left side of mutating operator isn't mutable: 'gpa' is a 'let' constant
The problem is that gpa is a constant, you can't modify its value. And the "+=" operator means "increase gpa's value by totalGradePoints", it is trying to increase the value of gpa. What you probably mean to do is make averageGPA equal the sum of gpa and totalGradePoints. For that you would do this:
let averageGPA: Double = gpa + totalGradePoints

How to add integer to array (with explicite int index) in swift?

I read swift handbook and was trying to do some exercises. But I run into a problem and I do not know if I do something wrong or if xCode 6 beta is just buggy.
// Playground - noun: a place where people can play
import Cocoa
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var lastLargest = Integer[]()
var index = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
//lastLargest[index] = number
index++
largest = number
}
}
}
index
lastLargest
largest
As soon as I uncomment lastLargest[index] = number I do not get any results on right side in playground. Nor I get any infos about index, lastLargest or largest.
Following example does not work either:
var index2 = 0
var lastLargest2 = Integer[]()
lastLargest2[index2] = 1
index2++
lastLargest2[index2] = 2
You are appending using an out of bound array-index. Don't do that. Instead, use append:
lastLargest.append(number)
From Apple's documentation:
You can’t use subscript syntax to append a new item to the end of an array. If you try to use subscript syntax to retrieve or set a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error.
When you're using explicit indexes (subscript notation) to set values in a mutable array, some value must already exist in that array at that index. When you use subscript notation, you're essentially using a 'set', rather than a 'set and add if necessary'.
As a result, you should be using:
lastLargest.insert(number, atIndex: index)
If you want to insert a new item. This will let you insert an item at the specified index, assuming your collection's size is already greater than or equal to the index you're trying to replace.