I ran into an error after trying a few things, which I comment in my code:
func findAllCollection<T: Equatable>(_ stuff: [T],_ searchTerm: T) -> [AnyCollection<Any>] {
var items = [AnyCollection<Any>]()
for i in 0..<stuff.count {
if stuff[i] == searchTerm {
items.append(i) // error Cannot convert value of type 'Int' to expected argument type 'AnyCollection<Any>'
// items.append(AnyCollection<Any>(i)) // I also tried this instead...error here too: No exact matches in call to initializer
}
}
return items
}
I used Collection instead of AnyCollection; Iâm not sure if one is better than the other for this. I will say youâre using it wrong, though. The idea here is that stuff should be a collection instead of an array, so that the function will work with different kinds of collections. And while the function no longer returns an array of Integers, it does not return an array of Collections either.
Edit: I take that back, Iâm not sure you want to be using AnyCollection here. I tried modifying my solution to use AnyCollection & while I was able to convert an array to an AnyCollection to pass into the function, I got some rather strange results back.
OK, the strange results are because Swift (or at least the playground) apparently doesnât have a way to print the results in a meaningful way when the collection is not an Array. I tried my original solution with a Set instead of an Array and had the same problem. I was able to use the returned value in the code to index into the collection, so it does appear that my solutions work (both the Collection version and the AnyCollection version), you just canât directly print the results the way you could with the Silver challenge.
When I wrote a version using AnyCollection, it looked very much like that. You just need to specify that the collection is a collection of items of type T:
func findAllGold2<T: Equatable>(_ input: AnyCollection<T>, _ searchTerm: T) -> // (you still need to work on the return type)
But with Collection, the declaration looks completely different. Since Collection is a protocol like Equatable is, I ended up having to put that inside the angle brackets as a type constraint on the first argument:
func findAllGold<TC: Collection>(_ input: TC,
but then that changed the way I declared searchTerm, and also changed how I set up the Equatable constraint on the items in the collection.
I tried Set as my return type and got this to build, but I donât know if itâs correctâŚ
func findAllCollection<T: Equatable>(_ stuff: AnyCollection<T>,_ searchTerm: T) -> Set<T> {
var items = Set<T>()
for i in 0..<stuff.count {
if stuff as! T == searchTerm {
items.insert(i as! T) // error Cannot convert value of type 'Int' to expected argument typ 'AnyCollection<Any>'
// items.append(AnyCollection<Any>(i)) // I also tried this instead...error here too: No exact matches in call to initializer
}
}
return items
}
But ran into build errors when I tried it outâŚ
let membersTom2 = findAllCollection(cast, "Tom") // Cannot convert value of type '[String]' to expected argument type 'AnyCollection<String>'
The return type on your function is still wrong. I guess using a Set is OK but it canât be a Set of things of type T, it needs to be a set of things that can be used to index into whatever container was passed in as stuff. And it canât be a Set of Integers either - you canât use integers as indexes into a set, for example. Different containers have different ways of indexing into them and the return type needs to be able to accommodate any of them. (Well, any collections that can store Equatable elements - I donât think you need to worry about Dictionaries, for instance.) The hint the assignment gives you is suggesting a solution to this problem.
You also have a problem with your if statement. You canât say âstuff == searchTermâ, you need to say âstuff[i] == searchTermâ but again, that isnât going to work for all collections when i is an integer (which is what the for loop sets it up to be). You need to find a different way to loop through the container.
What exactly is âan array of an associated type of the Collection Protocolâ if itâs neither an AnyCollection nor a Set?
A Slice? But after trying to understand the Collection protocol documentation, I donât think that meets the needs for this exercise.
Finally, when I revert back to this:
var items = AnyCollection<T>()
I get an error stating âMissing argument for parameter #1 in callâ and Xcode recommends âInsert â<#AnyCollection#>ââ as a Fix. So I did, and it gives me
var items = AnyCollection<T>(<#AnyCollection<T>#>) // `Ambiguous use of 'init(_:)'`
âŚwhich really doesnât look correct.
Iâve spent almost a week trying to get this right. And Iâm still struggling to break through to the correct solution.
Wow, no, that suggestion doesnât look correct to me either. But you need to get the function signature correct before you worry about the code inside it.
The Collection protocol defines several associated types, which are types related to the collection. The one that will be most useful here is Index, which will be a type that can be used to index the collection. If your function has an input of AnyCollection<T>, a value of type AnyCollection<T>.Index can be used to index into that collection. Your function should be returning an array of those values.
This is part of the code I wrote to test my implementation:
let numbers = Set<String>(["One", "Two", "Three", "three", "Four"])
let numberCollection = AnyCollection(numbers)
let indexes = findAllGold2(numberCollection, "Three")
for index in indexes {
print(numberCollection[index]) // prints "Three"
}
I tried applying index, but I canât find an equivalent method to append to add it to the array I want to return:
func findAllCollection<T: Equatable>(_ stuff: AnyCollection<T>,_ searchTerm: T) -> [AnyCollection<T>.Index]{
var items = [AnyCollection<T>.Index]()
for thing in stuff {
if thing == searchTerm {
items.append(thing) // No exact matches in call to instance method 'append'
}
}
return items
Iâm still flailing around here. What I would really find useful is a full-proof sequence to approach any programming exercise besides
Retry > Fail > Fail > Fail > Fail > Fail
Iâve tried StackOverflow and all I see in results are either inapplicable to what Iâm trying to solve or too difficult to understand.
Iâve stated my gripes with Apple Developer Documentation in the past.
Finally, the impression Iâve carried for a very long time is: one has to know everything about programming all at once before learning how to program.
Thatâs the most frustrating part about all of this.
You have items, which is an array of type AnyCollection<T>.Index, and you have thing which is of type T. You canât put thing into that array, you need to put the index of thing into the array.
AnyCollection has a property indices which is a collection of all the valid indices into the collection. So if you iterate through stuff.indices you can find out which ones point to a value matching searchTerm and put those indices into items.
Thatâs what programming is, especially when youâre learning. Thatâs what any learning experience is like - you fail a lot in the beginning. As you get more experience you fail less but you still fail. It took me about 40 minutes to figure out my first solution to this exercise and I made several wrong attempts before getting it right.
You donât need to know everything about programming. You do need to get used to the idea that at any time you could run into something you donât know how to do and you suddenly have to switch back into learning mode to figure it out, and sometimes that can involve a lot of trial & error.
If youâre talking about the test code I posted earlier, you canât add more entries of âThreeâ to the set numbers. Sets are collection of unique values, so any given string can only occur once in the set. Thatâs why my set had âThreeâ followed by âthreeâ. If you try to initialize a Set with an array that has a duplicate entry, it will silently throw away the duplicate:
let numbers = Set<String>(["One", "Two", "Three", "Three", "Four"])
results in the set {âTwoâ, âFourâ, âOneâ, âThreeâ} (although the order changes every time I run my playground, which is to be expected with an unordered collection).
And anyway firstIndex doesnât do what you want. Suppose you passed in an array like [1, 2, 3, 4, 1, 5, 6]. If you tell your function to search for the number 1, you should get back two indexes - one for the first value and one for the fifth value, but your function would return two indexes to the first value.
I think the reason they deprecated index(of:) is that the name was misleading. If you read the description of the function it was returning the first index, so it was really identical to firstIndex(of:) but the name might have made people think it was not returning the first index.
Like I said in my previous post, you need to iterate through the container indices, not the container values.
func findAllCollection<T: Equatable>(_ stuff: AnyCollection<T>,_ searchTerm: T) -> [AnyCollection<T>.Index]{
var items = [AnyCollection<T>.Index]()
var i = stuff.startIndex
while i != stuff.endIndex {
if stuff[i] == searchTerm {
items.append(i)
}
i = stuff.index(after: i)
}
return items
}
let numbers = AnyCollection<String>(["One", "Two", "Three", "three", "Four", "Three"])
let numberCollection = AnyCollection(numbers)
let indexes = findAllCollection(numberCollection, "Three")
for index in indexes {
print(numberCollection[index]) // prints "Three" twice