I want to write a generic method in an Array extension which takes a parameter type that is also an array where the element types are the same (for the caller of the array extension and the parameter). This is kind of what I mean (but none of them works):
extension Array {
func doSomethingWithAnotherArray(arr: Self) {
}
}
extension Array {
func doSomethingWithAnotherArray<T: Array<Element>>(arr: T){
}
}
extension Array {
func doSomethingWithAnotherArray<T: Array<U>, U>(arr: T) where U == Element{
}
}
So I can use it as:
let x = [1, 2]
let y = [3, 4]
x.doSomethingWithAnotherArray(arr: y)
Since x and y has the same elements.
Just simply pass parameter of type Array
extension Array {
func doSomethingWithAnotherArray(arr: Array) {
... // do something
}
}
[Int].doSomethingWithAnotherArray(arr: [Int]) // works
[Int].doSomethingWithAnotherArray(arr: [String]) // doesn't work
If the only restriction is that Element of the argument is the same as that of the receiver:
extension Array {
func doSomethingWithAnotherArray(arr: Array<Element>) {
// …
}
}
edit: As seen in this answer, a simple Array suffices, since this function is not generic in itself, and in context of the generic type, Array is already specialized to the type of the receiver.
If you need to place other restrictions on Element, use extension Array where Element ….
Related
My first attempt to use swift generics:
extension RealmSwift.List where Element == Object {
// #deprecated use RealmSwift.List<>
func arrayo<T: Object>() -> [T] {
var res: [T] = []
for card in self {
res.append(card) <- here I got
No exact matches in call to instance method 'append'
}
return res
}
convenience init<T: Object>(objects: [T]) {
self.init()
for card in objects {
append(card)
}
}
}
what's a good way to write this adapter once and for all?
Notice the where Element. You can refer to the type of the list items using Element, so you do not need to set up another type parameter T. card is of type Element not T, so you cannot add it to the Array<T>. There is no guarantee that T and Element are equivalent so the compiler doesn't allow it. The same applies for your convenience init.
extension RealmSwift.List where Element == Object {
// #deprecated use RealmSwift.List<>
func arrayo() -> [Element] {
var res: [Element] = []
for card in self {
res.append(card) // Now you are adding an `Element` to the array of `Element` so it will work.
}
return res
}
convenience init(objects: [Element]) {
self.init()
for card in objects {
append(card)
}
}
}
But generics are not really useful here because you are constraining Element to Object already. So there is only one potential type - You could make arrayo() and the init use Object directly.
To make this useful do
extension RealmSwift.List where Elemtn: RealmCollectionValue
I want make a custom Enumerated function, for that reason I need to know the type of value in my array, how can I read that information, here what I tried but need correction:
extension Array {
typealias EnumeratedType<T> = [(index: Int, item: T)] // T: is the type of Elements of Array!
func customEnumerated() -> EnumeratedType<ElementType> {
var enumeratedTypeArray: EnumeratedType<ElementType> = EnumeratedType()
self.sorted().forEach { item in
enumeratedTypeArray.append((index: enumeratedTypeArray.count, item: item))
}
return enumeratedTypeArray
}
}
From the documentation, we can see that Array is declared as:
struct Array<Element>
So the element type is called Element. You can declare your method like this:
extension Array where Element : Comparable {
// note that this doesn't need to be generic, because the type alias is in the scope of the array
typealias EnumeratedType = [(offset: Int, element: Element)]
func customEnumerated() -> EnumeratedType {
// you can simplify the forEach call to this
.init(sorted().enumerated())
}
}
Note that the constraint Element : Comparable is required, as that makes the parameterless overload of sorted available.
I would suggest that you declare the extension on the most general type possible, so that your method is available to as many types as possible, and return an EnumeratedSequence (same as what enumerated returns) instead. This way you don't need your own EnumeratedType type alias.
// It just so happens that the element type of a Sequence is also called "Element"
// See https://developer.apple.com/documentation/swift/sequence/2908099-element
extension Sequence where Element : Comparable {
func customEnumerated() -> EnumeratedSequence<[Element]> {
sorted().enumerated()
}
}
As you can see in the documentation, the name of Array’s sole generic type parameter is Element.
I have a String like LINNIIBDDDN, basically a series of tokens. I'd like to use multiple iterators, one for each token type. I'd like to have each iterator ignore the tokens that don't belong to it. That is, I want to call something like next_ish(), to advance the iterator to the next element of its particular token. So if the Niterator is at index 3, and I call next_ish(), I want it to go to index 10, the next N, not the I at index 4. I have some code that already works, but it's a lot of code, it makes the String into an array, and I have subclassed iterators, basically hand-written, with no help from Swift, although I'm sure the Swift iterators are more stable and thoroughly tested. I'd rather use their code than mine, where possible.
It seems like it should be easy just to extend String.Iterator and add next_ish(), but I'm at a loss. My first naive attempt was to extend String.Iterator. I get the error Constrained extension must be declared on the unspecialized generic type 'IndexingIterator' with constraints specified by a 'where' clause. I went looking to figure out what kind of where clause to use, and I haven't found anything.
There are a lot of answers here on SO, about extending arrays and generics, pulling all the elements of a certain type into an array of their own, even some answers about specialized for...in loops, but I can't find anything about extending iterators. I've read through Collections.swift and haven't found anything helpful. Is it possible to extend String.Iterator? That would make my life a lot easier. If not, is there some sort of built-in Swift mechanism for doing this sort of thing?
String.Iterator is (implicitly) defined as
typealias Iterator = IndexingIterator<String>
and the error message
Constrained extension must be declared on the unspecialized generic type 'IndexingIterator' with constraints specified by a 'where' clause
means that we must define extension methods as
extension IndexingIterator where Elements == String { }
Alternatively (with increasing generality):
extension IndexingIterator where Elements: StringProtocol { }
extension IndexingIterator where Elements.Element == Character { }
I haven't found a way to access the underlying collection (or position)
from within an extension method, the corresponding members are defined as
“internal”:
public struct IndexingIterator<Elements : Collection> {
internal let _elements: Elements
internal var _position: Elements.Index
// ...
}
What you can do is to pass the wanted element to your “next-ish” method,
either as the element itself, or as a predicate:
extension IndexingIterator where Elements.Element == Character {
mutating func next(_ wanted: Character) -> Character? {
while let c = next() {
if c == wanted { return c }
}
return nil
}
mutating func next(where predicate: ((Character) -> Bool)) -> Character? {
while let c = next() {
if predicate(c) { return c }
}
return nil
}
}
Example usage:
var it1 = "ABCDABCE".makeIterator()
print(it1.next("C") as Any) // Optional("C")
print(it1.next() as Any) // Optional("D")
print(it1.next("C") as Any) // Optional("C")
print(it1.next() as Any) // Optional("E")
print(it1.next("C") as Any) // nil
var it2 = "LINnIIBDDDN".makeIterator()
while let c = it2.next(where: { "Nn".contains($0) }) {
print(c, terminator: ", ")
}
print()
// N, n, N,
But actually I would consider String.Iterator being an IndexingIterator an implementation detail, and extend the IteratorProtocol instead:
extension IteratorProtocol where Element: Equatable {
mutating func next(_ wanted: Element) -> Element? {
while let e = next() {
if e == wanted { return e }
}
return nil
}
}
extension IteratorProtocol {
mutating func next(where predicate: ((Element) -> Bool)) -> Element? {
while let e = next() {
if predicate(e) { return e }
}
return nil
}
}
That makes it usable for arbitrary sequences. Example:
var it3 = [1, 1, 2, 3, 5, 8, 13, 21, 34].makeIterator()
while let e = it3.next(where: { $0 % 2 == 0} ) {
print(e, terminator: ", ")
}
print()
// 2, 8, 34,
extension Array where Element: StringLiteralConvertible{
func spliteByPrefix() -> [Element]{
for item in self{
}
return []
}
}
I want to write an extension of Array whose Element is always a String. And in my spliteByPrefix() function, i want to use maybe item.characters or something else which a String has. How?
As for now, you cannot write an extension of Array "whose Element is always a String" in Swift.
But you can write some extension which has nearly the same functionality.
Write your own protocol, which String can conform to:
protocol MyStringType {
var characters: String.CharacterView { get }
//You may need some other properties or methods to write your extension...
}
// Make `String` the only type conforming to `MyStringType`.
extension String: MyStringType {}
And write an extension where Element conforms to the protocol.
extension Array where Element: MyStringType {
func spliteByPrefix() -> [Element]{ //You really want to return, `Array<Element>`?
for item in self {
for ch in item.characters {
//Do something with `ch`.
_ = ch
}
}
return []
}
}
In this example Element is always a Int. I use the elements themselves of the array
extension Array where Element: IntegerType {
func toString() -> [String] {
var result = [String]()
for value in self {
result.append(String(value))
}
return result;
}
}
Example of use:
let numberInt = [23, 10, 79, 3]
let numberString = numberInt.toString()
Right now I want to be able to see if an object is included inside an Array so:
func isIncluded<U:Comparable>(isIncluded : U) -> Bool
{
for item in self
{
if (item == isIncluded)
{
return true
}
}
return false
}
If you notice this function belongs to an Array extension. The problem is if add it to this:
extension Array{
}
I receive the following error:
Could not find an overload for '==' that accepts the supplied arguments
I understand that I could probably need to tell what kind of objects should be inside the Array like so: T[] <T.GeneratorType.Element: Comparable>. But it doesn't work as well:
Braced block of statements is an unused closure
Non-nominal type 'T[]' cannot be extended
Expected '{' in extension
With Swift, we'll need to think whether there's a function that can do the trick -- outside the methods of a class.
Just like in our case here:
contains(theArray, theItem)
You can try it in a playground:
let a = [1, 2, 3, 4, 5]
contains(a, 3)
contains(a, 6)
I discover a lot of these functions by cmd-clicking on a Swift symbol (example: Array) and then by looking around in that file (which seems to be the global file containing all declarations for Swift general classes and functions).
Here's a little extension that will add the "contains" method to all arrays:
extension Array {
func contains<T: Equatable>(item: T) -> Bool {
for i in self {
if item == (i as T) { return true }
}
return false
}
}
To add, the problem is that T is already defined and the Array's definition of T does not conform to Equatable. You can either accomplish what you want by casting (like the accepted answer), and risking an invalid cast, or you could pass in a delegate where no casting would be required.
Consider modifying like so:
extension Array {
func contains(comparator: (T)->Bool) -> Bool {
for item in self {
if comparator(item) {
return true
}
}
return false
}
}
Example usage:
class Test {
func arrayContains(){
var test: Int[] = [0,1,3,4,5]
//should be true
var exists = test.contains({(item)->Bool in item == 0});
}
}
Not to say that it's impossible, but I haven't yet seen a way to extend structs or classes to put conditions on the original generics, for instance to guarantee Equatable or Comparable on an Array. However, for your particular issue, instead of extending, you can do something like the following:
var arr = [1, 2, 3]
var isIncluded : Bool = arr.bridgeToObjectiveC().doesContain(1)