How to instantiate an Array of class in swift? - arrays

Hey I gotta problem when I declared and created an array of my class.
I declared an array like this
var _mScoringData = [ScoringData]()
And here's 'ScoringData' class
class ScoringData{
let num : Int!
let question : String!
var yes : Bool!
var no : Bool!
init(num:Int,question:String){
self.num = num
self.question = question
self.no = false
self.yes = false
}
}
I made a function to create an array of instances, following code is an implementation of the function
func createScoringDatas(){
for var index = 0; index < scoreDataCount; ++index{
_mScoringData.append(ScoringData(num:index,question: _mQuestionData.Questions[index]))
}
}
Build was good however the array does not created and when I debugged nothing was on the heap.
I want to know how to solve this problem.
thanks.

It’s impossible to tell without your full code, but chances are the reason is because your variable scoreDataCount is zero. If I take your code, and make sure scoreDataCount is non-zero, it works fine.
Is scoreDataCount just a count of the number of questions in _mQuestionData.Questions? If so, you may want to consider the following alternative to the C-style for and array subscripting:
for (index, question) in enumerate(_mQuestionData.Questions) {
_mScoringData.append(ScoringData(num: index, question: question))
}
enumerate(someSequence) takes a sequence (such as an array), and creates a new sequence of pairs, an index starting at zero, and the elements of the sequence. You can then loop over that with for…in which can look a lot cleaner and help avoid bugs (like the one you’ve hit).
Once you understand this, it’s a short step from there to scrap the for loop altogether and replace it with a map:
_mScoringData = map(enumerate(_mQuestionData.Questions)) {
index, question in
ScoringData(num: index, question: question)
}
Note, this creates a map, rather than appending to an existing one, so you may no longer need your initial creation line.
Whenever you have a sequence of things, and you want to transform each element and put it in an array, that’s a case for map. You could argue that the for…in is clearer (especially if you’re not used to seeing map) but the benefit of map is it makes it completely obvious what your intent is - to transform the input into a new array.
The other benefit of this is, you could now declare _mScoringData with let instead of var if it turns out you never need to change it once it’s first created.
P.S. you probably don’t need those ! after all the member variables in ScoringData. Those are creating implicitly unwrapped optionals, and are only needed in very specific circumstances. You might see them sometimes elsewhere when they’re needed but you don’t need to copy that on every member variable.

I'm not sure why you're seeing the problem you're having. I took the code and pasted it into a playground to run it and it works fine. I'm pasting my slightly modified version of the code below. My only changes were to declare the scoreDataCount value and to change the source of the questions to an array of strings.
class ScoringData{
let num : Int!
let question : String!
var yes : Bool!
var no : Bool!
init(num:Int,question:String){
self.num = num
self.question = question
self.no = false
self.yes = false
}
}
let scoreDataCount = 5
var _mScoringData = [ScoringData]()
let questions = ["One", "two", "three", "four", "five" ]
func createScoringDatas() {
for var index = 0; index < scoreDataCount; ++index{
_mScoringData.append(ScoringData(num:index,question: questions[index]))
}
}
createScoringDatas()
print(_mScoringData.count)
print(_mScoringData[2].question)
The last 2 print statements output 5 and "three" respectively which is what I would expect.

Related

How to use an array of functions in Swift

I have read all the posts I can find here about arrays of functions - great you can do it. I figured. But none of the posts show practically how to use them (at least not what I'm trying to do). Here's what I want - they can all take the same args, but that's not a requirement.
This article is close, and will allow me to loop through to execute each function (which meets the first goal).
https://stackoverflow.com/a/24447484/11114752
But... what if I want to execute a single function by reference?
In other words, how to call just the referenced Arity2 function - for example:
// None of these work (with or without the parameter labels)
funcs.Arity2(n: 2, S: "Fred) // value of type [MyFuncs] has no member .Arity2
funcs[Arity2](n: 2, S: "Fred") // no exact matches to call in subscript
funcs[.Arity2](n: 2, S: "Fred") // Cannot call value of non-function type...
let fn = funcs.first(where: { a whole ton of permutations here to try to match Arity2 }) -- a whole lotta frustrating nope...
Help, please! Nothing I've tried works. The pre-compiler just goes in circles making suggestions that don't pan out and it will not compile.
EDIT:
The reason for the array in the first place is that I'm going to have a quite a few functions, and I don't know what they all are in advance. Essentially, I want a plugin type of architecture. Where I can add to the list of functions (ideally within an extension of the class, but that's another problem..) and not change the processing loop that executes each function in order.
I assume you need something like
_ = funcs.first {
if case let MyFuncs.Arity2(f) = $0 {
f(2, "Fred")
return true
}
return false
}
It can be achieved in a much simpler way if you know the position of the function in the array.
Assuming you have:
func someFunc(n: Int, s: String) {
print("call \(n) \(s)")
}
var funcs = [MyFuncs.Arity2(someFunc)]
you can do:
if case .Arity2(let f) = funcs.first {
f(2, "Fred")
}
By replacing funcs.first with funcs[i] you can access the i-th index (first make sure it does exist).

Populating Array in Swift 4 with for-loop without using the index

I need to populate an Array (already declared and initialized) using a for loop in order to create a determinate amount of items.
I ended up with the following code:
func createValues() -> Array<Int> {
let usableRange:Range = 6..<11;
var arrayOfValues: Array<Int>=[]; //Array declared and initialized
for i in 0..<10 {
arrayOfValues.append(random(usableRange));
print(arrayOfValues[i]);
}
return arrayOfValues;
}
this code does what I expect it to do just fine. However, as soon as I comment out the line
print(arrayOfValues[i]);
Xcode throws the following warning:
Immutable value 'i' was never used; consider replacing with '_' or
removing it
If I accept the suggestion the code works, but not as fine as it did before.
I'm just transitioning from Obj-C to Swift and I don't really know what the proper way to do this should be. Any help would be appreciated. Thanks in advance.
P.S. I'm aware that I don't need semicolons anymore, but old habits die hard, I guess...
Since you don't use i, you can just write
for _ in 0 ..< 10
The _ means "yes, there is a value, but I don't care about it", here and in many other situations.
If you want just a good alternative for your code, I'm offering you this:
var i: Int = 0
while i < 10 {
arrayOfValues.append(random(usableRange))
i += 1
}
If the goal is to generate an array of random numbers in your given range, I would suggest you simply generate it directly. There is no need for the for loop.
let usableRange = UInt32(6)..<UInt32(11)
let arr = (0..<10).map { _ in Int(
arc4random_uniform(usableRange.upperBound - usableRange.lowerBound)
+ usableRange.lowerBound
)}
Array has a designated initalizer, which initalizes an array with a given size and a repeated value:
let values = Array(repeating: "VALUE", count: 5)
print(fiveZs)
// Prints "["VALUE", "VALUE", "VALUE", "VALUE", "VALUE"]"
Source: Apple Documentation

Adding arrays of different types

Is there more elegant way of adding two arrays of different types in Swift?
My first try was to downcast an array to Any and just add it.
class BasicClass {
var name : String = ""
var number : Int = -1
init(name : String, number : Int){
self.name = name
self.number = number
}
}
let basicClass1 = BasicClass(name: "bc1", number: 1)
let basicClass2 = BasicClass(name: "bc2", number: 2)
let basicClass3 = BasicClass(name: "bc3", number: 3)
let basicClasses : [BasicClass] = [basicClass1, basicClass2, basicClass3]
for bc in basicClasses{
print(bc.name)
}
let strings : [String] = ["one", "two", "three"]
var anyArray : [Any] = strings as [Any] + basicClasses as [Any]
I'm asking about this last line.
Creating [Any] this way is a mistake in Swift. There are places that [Any] pops up because of bridging to Cocoa, but you should never create them independently. It is incredibly rare that you mean "an array of absolutely anything at all." If you don't mean "absolutely anything at all is fine," you don't mean Any. (For an example of something that means that, consider print, which correctly accepts "absolutely anything at all.")
If you want "an array of strings or BasicClasses" then there's a type for that. It's an enum:
enum Element {
case basicClass(BasicClass)
case string(String)
}
let combined = strings.map(Element.string) + basicClasses.map(Element.basicClass)
This wraps your strings and you BasicClasses in the Element type, and then creates an [Element].
(Note that I've used + here to combine the arrays because it's just so convenient in this case, but it can create significant problems with compilation time, and it's inefficient because it often forces too many copies. You generally should avoid using + for anything but numbers. But in this example it's just so much prettier than the alternative using .extend.)

Check validity of element in an array of objects and then retrieve that object and its index?

I have a simple array of objects:
var cars: [Car] = [Car(ID: "010", name: "car1"), Car(ID: "230", name: "car2"), Car(ID: "350", name: "car3")]
The user will use a UITextfield to enter an ID.
I will then have to check the array to see if there is an object that has this exact ID, if such an object exists I need to retrieve the object and the index where it is located.
In addition I need to update a flag variable to know whether the user entered a correct or incorrect number. As I will use this to change other aspects of the view controller (such as lay-out).
I currently have this function that receives the ID as parameter and then returns a tuple for another function. The carObject and indexOfCar are global variables that have to be updated:
func checkIdMatch(IdInput: String) -> (Bool, Car?) {
var returnObject: Car?
var tuple = (false, returnObject)
for (index, car) in cars.enumerated() {
if car.carId == IdInput {
returnObject = car
tuple = (true, car)
carObject = car
indexOfCar = index
flag = 2 //correct ID
} else {
flag = 1 //wrong ID
}
}
self.tableView?.reloadData()
return tuple
}
I tried to use a where statement after the for()-statement, but then I couldn't update the flags correctly. What am I doing wrong?
Any help is immensely appreciated!
There's a lot going on here, I'm going to try to address everything, one at a time:
In your code, you never break your loop early when you find a match.
There are obvious performance implications
Has the (probably undesired) side effect that flag keeps getting set over and over again. Thus, flag will only be 2 if the correct ID is found last in the array (so that there's no next element for which it'll be set back to 1).
Your flag is a raw integer. It should probably be an enumeration.
Your cars array has an unnecessary type annotation : [Car] can (and should) be left to the compiler to infer.
Your (Bool, Car?) tuple is superfluous. It's a clever idea in feneral: pairing a data value with a data value (such as a Car instance, in this case) with a Bool value that represents whether or not the data value is non-null. Apple liked this design so much, in fact, that this is exactly how Optional works! You just need to return Car?, which can either be a valid instance, or nil.
Your returnObject variable is never used, and ironically, never returned.
Your function called checkIdMatch(IdInput:) not only checks if an ID matches, but also removes it from the array, changes global state, and refreshes your table view. This doesn't sound like "checking" to me!
Your function name, and its parameter use Id, whereas your Car uses ID. Inconsistent.
Your function's parameter doesn't need the Input suffix. We know that parameters are inputs.
carObject and indexOfCar probably shouldn't be globals, but I don't have enough context to be able to tell. In any case, wouldn't indexOfCar always be inaccurate? You're storing the index where the Car is, but then you immediately remove the car, so the index is now wrong.
You use copious amounts of so called "side effects". This is when a function has an effect apart from just processing its parameters into some return value. This is unavoidable in many cases, but having lots of side effects make code really hard to work with, hard to maintain, and hard to extend. Wherever possible, try to minimize side effects and reliance on global state. Ideally, you should try to write "pure" functions. Those that have no effect beyond returning a result. They're much, much easier to work with.
Here's my first attempt:
struct Car {
let id: String
let name: String
}
enum Flag: Int { //TODO: Name me!
case original
case noMatch
case match
}
var flag = Flag.noMatch;
var cars = [Car(id: "010", name: "car1"), Car(id: "230", name: "car2"), Car(id: "350", name: "car3")]
func removeCar(ID: String) -> Car? {
guard let index = cars.index(where: {$0.id == id}) else {
flag = .noMatch //TODO: side effects make kittens cry
return nil
}
flag = .match //TODO: side effects make kittens cry
return cars.remove(at: index)
}
print("Before:\r\n\(cars)\r\n\r\n")
print("Removed:\r\n\(removeCar(id: "350"))\r\n\r\n")
print("After:\r\n\(cars)\r\n\r\n")
You can try it online, here.

Swift Dictionary lookup causing compile-time error

I'm dipping my toe into Swift, but have run into an issue that has me slightly confused. Given an integer index I'm trying to fetch the corresponding key of a Dictionary and return the value associated with it.
Using the following structure as an example:
Class CustomClass {
private var collection: [String: [SifterIssue]] = ["MyStringKey": [MyCustomCollectionClass]()]
/* ... */
}
I tried to solve the problem like so:
var keys = Array(self.collection.keys)
var key: String = keys[section] as String
return self.collection[key].count // error is flagged here
But found that this results in a compiler error, which states that 'String' is not convertible to 'DictionaryIndex'. Stumped, I tried a slightly more verbose solution and was surprised to find that this compiled and worked without issue.
var keys = Array(self.collection.keys)
var key: String = keys[section] as String
var collection: [MyCustomCollectionClass] = self.collection[key]! as [MyCustomCollectionClass]
return issues.count
Can anyone explain to me why the first solution refuses to compile?
As #Zaph said, ignoring potential fatal errors is a bad idea and it's something that swift was, in part, designed to help with. This is the most "swifty" code I could come up:
func collectionCount(#section: Int) -> Int? {
switch section {
case 0..<collection.count: // Make sure section is within the bounds of collection's keys array
let key = collection.keys.array[section] // Grab the key from the collection's keys array
return collection[key]!.count // We can force unwrap collection[key] here because we know that key exists in collection
default:
return nil
}
}
It uses the range/pattern matching feature of swift's switch statement to make sure that section in the bounds of collection's keys array; that felt more "swifty" than using if, mainly because I couldn't find a way to use swift's Range in an if statement. It also uses collection.keys lazy property array as a shortcut instead of creating a new Array with Array(collection.keys). Since we've already made sure that section is within the bounds of collection.keys, we can forcibly unwrap collection[key]! when we get its count.
Just for fun, I also made a generic function that takes a collection as input to generalize things:
func collectionCount<T,U>(#collection: [T:[U]], #section: Int) -> Int? {
switch section {
case 0..<collection.count: // Make sure section is within the bounds of collection's keys array
let key = collection.keys.array[section] // Grab the key from the collection's keys array
return collection[key]!.count // We can force unwrap collection[key] here because we know that key exists in collection
default:
return nil
}
}
[T:[U]] basically says that collection needs to be a Dictionary with key T whose values are an Array of U.
Ignoring the fatal potential error is a really bad idea. The whole reason for Optionals is to prevent crashes at runtime.
func collectionCount(#section: Int) -> Int? {
var keys = Array(self.collection.keys)
if section < keys.count {
var key = keys[section] as String
println("key: \(key)")
return self.collection[key]!.count
}
else {
// handle error here
return nil
}
}
Throwing in "!" unwrapping without knowing that the value can never be nil is much worse than the Objective-C handling of nil. If this becomes the standard way of handling Optionals by a substantial number of developers Swift will be a disaster. Please do not do this.

Resources