Reputation: 41
I am currently attempting to learn how to code swift via treehouse.com and so far I am enjoying it. I just came to my first "code challenge" as they call it that I somewhat struggled on. As the title suggested, the started with an empty array as such:
var results: [Int] = []
That is fine and dandy. The goal is to then write a for in loop which finds the multiples of 6 for the numbers 1-10 and then to append those values to the array. I eventually did figure it out, but I am not sure that I did it in the ideal way. I'd appreciate any criticism. My code will be included below. Do bear in mind that I am a total beginner to Swift and to coding in general. Thanks in advance for any suggestions/tips.
var results: [Int] = []
for multiplier in 1...10 {
let multiples = (multiplier * 6)
print(multiples)
results.append(multiples)
}
The code executes properly and the array is appended with the values, but again, I am unsure if there is a better way to do this.
Upvotes: 4
Views: 9848
Reputation: 72450
For your first question, Is there batter way or best way to append objects in array in for in
loop is already explain by @Alexander, But if check properly he is still at last doing the same way you are doing the difference is only that he has specified the capacity of array, So the way you are appending object in array is look like perfect but for that you need to write to much code.
Now to reduce your code and do what you are currently doing in a Swifty way you can use built in map
function for that.
let result = (1...10).map { $0 * 6 }
print(result) // [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]
First (1...10)
will create CountableClosedRange
after that we are calling map(_:)
closure with it. Now map
will sequentially take each element from this CountableClosedRange
. So $0
will take each argument from CountableClosedRange
. Now we are multiplying that element with 6 and returning the result from the closure and generate the result according to its return value in this case it will create Array
of Int
.
Upvotes: 11
Reputation: 63349
This is a great question, and a perfect opportunity to explain map
and array reallocation costs.
Arrays have two important properties you need to understand.
The first is their count
. The count
is simply the number of items in the array.
The other is the capacity
. The capacity is the number of elements that can fit the memory that has been allocated for this array. The capacity
is always ≥ the count
(d'uh!).
When you have an Array
of say, capacity
10, and count
5, and you try to append to it, you can do that right away. Set the 6th element to the new value, and boom, you're done instantly.
Now, what happens if you have an Array
of capacity
10, and count
10, and you try to append to it? Well there's no more room for new elements given the memory the array currently has allocated. What needs to happen is that the Array
has to ask the operating system for a new chunk of memory that has a capacity
that's high enough to fit at least the existing elements, plus the new incoming element. However, this new chunk of memory is empty, so we need to copy over what we had in our old small chunk of memory, before we can do the append. Suppose our new chunk has capacity
20, and we copy our 10 old elements. We can now add in our new element in the 11th slot.
Notice we had to copy the elements from our old memory into the new memory. Well if our count is 10
, that's 10
copy operations. This means that appending to an array that's at capacity is much slower than appending an array with available capacity.
Suppose we go on to add another 1000 elements.
- Our array will fill its new memory at count
20, so it'll need to reallocate a new piece of memory, do 20 copy operations. Suppose this new piece of memory has capacity
40.
- Our array will fill its new memory at count
40, so it'll need to reallocate a new piece of memory, do 40 copy operations. Suppose this new piece of memory has capacity
80.
- This process will repeat at count
= 80, 160, 320, 640, until we finally have all 1011 elements in our array of capacity 1280. This took 10 + 20+ 40 + 80 + 160 + 320 + 640 = 1270
copy operations in total!
What if we were smart about this, and knew in advance that we were about to add 1000 items, and could tell the array to resize itself all at once, to fit our new 1000 items?
Well we can do exactly that, with reserveCapacity(_:)
. It lets us tell the array to allocate a certain capacity for us, rather than it having to constantly resize on-demand as we keep stuffing it. Using this technique, we can ensure now copy operations occur at all!
Now let's apply that to your code. We can rewrite it like so, to ensure no array reallocaitons happen (even it 1...10
were something higher, like 1...10000000
)
let numbers = 1...10
let numberCount = numbers.count
var products = [Int]()
products.reserveCapacity(numberCount)
for number in numbers {
let product = (number * 6)
print(product)
products.append(product)
}
Now all that is quite a bit of code, for such a simple operation. It's also a pretty common operation. This is why map
exists, to simplify all this.
map(_:)
takes a closure which tells it what to do to each element. map(_:)
will take every element in your sequence (in this case, your CountableClosedRange<Int>
, 0...10
, but it can be an Array
, or any other Sequence
), run it through your closure to produce some result, and put the result in the final array.
Internally, map(_:)
works very similar to the code you wrote above. It creates a mutable array, reserves enough capacity for all the elements, and runs a for
loop which repeatedly appends to that array. The nice thing is that it hides all this logic from you, so all you have to see is this really simple statement:
let produces = (1...10).map{ $0 * 6 }
Closures are explained simply, and in great detail in the Swift Progamming Language Guide. I suggest you take a look when you have a chance!
Upvotes: 7