I have an array that contains a mixture of strings, arrays, and dictionaries. I would like to check if it contains a dictionary. I am trying the following, but receiving errors:
if array.contains(Dictionary<String, AnyObject>) {
}
How would I accomplish this?
You don't have to bother with overloading the contains method if you don't want to – there's already a version of it that can take a custom predicate to check against each element to determine whether it should be considered to be 'in the array'.
It has the advantage of returning early as soon as it finds a match, unlike filter. You could still turn this into an extension method if you really want – but in my opinion it's so concise you shouldn't have to.
if array.contains({$0 is [String:AnyObject]}) {
print("contains dictionary")
} else {
print("doesn't contain dictionary")
}
#Ryan, the type does not have to conform to the Equatable protocol.
This would get the job done:
extension Array {
// This is a concise, yet inefficient implementation
func contains<T>(type type : T.Type) -> Bool {
return !filter({ $0 is T }).isEmpty
}
// Here is a more efficient implementation
func contains<T>(type type : T.Type) -> Bool {
var contains = false
for element in self {
if element is T {
contains = true
break
}
}
return contains
}
}
It can be used as such:
if array.contains(type: Dictionary<String, AnyObject>.self) {
// Run code if the array holds a dictionary of the given type
}
This requires that the array elements conform to the Equatable protocol (which Dictionary doesn't).
You would have to extend contains with something like this:
extension Array {
func contains<T where T : Equatable>(obj: T) -> Bool {
return self.filter({$0 as? T == obj}).count > 0
}
}
Related
I am trying to create an Rx operator that works on arrays. I've tried this simple extension:
extension ObservableType where Element == Array<Any> {
func beat<U>(_ beat: Observable<U>) -> Observable<Element.Element> {
let lhs = self.flatMap { Observable.from($0) }
return Observable.zip(lhs, beat).map { $0.0 }
}
}
Now, I've noted Array<Any> because the otherwise I get
Reference to generic type 'Array' requires arguments in <...>, but this way the return value is of courses Observable<Any>.
Is it even possible to return an observable with the actual Element of the array?
How about this?
func beat<U>(_ beat: Observable<[U]>) -> Observable<[U]> where [U] == Element {
Observable.zip(self, beat).map { $0.0 }
}
Most likely you want to use the parametrized extensions that is not implemented in Swift 5.2.
As a workaround, you can create a global generic function that accepts Array<T> as the second parameter.
I have an array, within an array situation, and I want to constrain the internal Array's element type.
// this doesn't work but should illustrate what I want. Is there
// any way to constrain the Element type for an Array when it's
// already declared as the type for an Array
extension Array where Element == Array<Element2: SomeProtocol> {
}
Ultimately, I think that solving this problem will fix the following compile error
protocol Thingy {
associatedtype MeasurementType: Measurement
var topMeasurements: [[MeasurementType]] { get }
}
extension Thingy {
func doStuff() {
// COMPILE ERROR'[[Self.MeasurementType]]' is not convertible to 'Array<Array<Measurement>>'
let compileError = self.topMeasurements.measurements(forUUIDs: ["A"])
// Hack workaround
// The fact that this works leads me to believe that the constraint in the
// extension is inadequate. The extension wants [[Measurement]] and
// event though MeasurementType is a kind of Measurement the compiler won't
// allow it. The hope is that if we could write something like
// [[Element]] where Element: Measurement
let measurements: [[Measurement]] = self.topMeasurements
let thisWorks = measurements.doSomething(ids: ["A"])
print(thisWorks)
}
}
// I'm hoping that if I constrain the following that it'll fix my issue
// constrain this type ----------------------
// |
// \/
extension Array where Element == Array<Measurement> {
func doSomething(ids: [String]) -> [Measurement] {
// do something return some elements,
// I've removed any code that could cause confusion
return []
}
}
This is much easier to implement as an extension on all Collections rather than just on Arrays. I believe the extension you want is this:
extension Collection where Element: Collection, Element.Element: Measurement {
func measurements(forUUIDs: [String]) -> [Measurement] {
return []
}
}
The basic problem is that Swift lacks higher-kinded types, so you can't extend Array (because that's not a proper type). But you can extend Collection (because that's a PAT).
I have an array of dictionaries and just want to check if the array contains a certain value. However, I am getting this error message:
Contextual type '([Double : Double]) throws -> Bool' cannot be used with dictionary literal
Code:
if stored.contains(where: ([YAngle : ZAngle])) { // Error on this line
print("This data is already stored")
}
What am I doing wrong?
Your usage of contains(where:) is incorrect. This is an example of usage of that function that will not cause a complier error:
if stored.contains(where: { (key, value) -> Bool in
return true
}) {
// no op
}
What you probably want to do to check whether or not the dictionary contains a key-value pair is simply:
if stored[YAngle] == ZAngle {
print("This data is already stored")
}
contains(where:) takes a function as its argument. It's meant to be used when the array's element type doesn't conform to the Equatable protocol or if you need to do additional logic as part of the check. There's two ways you can do this:
[Double:Double] conforms to Equatable, so since you're just doing a simple comparison you can instead use contains(_:) (i.e. just remove the where:):
if stored.contains([YAngle : ZAngle]) {
print("This data is already stored")
}
If you're doing a more complex check, or if your type didn't conform to Equatable, you would pass in a function instead:
if stored.contains(where: { dict in
// Here you would do your logic and return a boolean based on the result.
// If you really wanted to do it this way with your current data
// you could do this:
return dict == [YAngle:ZAngle]
}) {
print("This data is already stored")
}
I need to do something like this:
extension Array {
func flat() -> Element { return self.flatMap { $0 } }
}
But there is problem with type inference:
'flatMap' produces '[SegmentOfResult.Iterator.Element]', not the
expected contextual result type 'Element'
Edit: Usage example:
[[1,2],[3,4,5],[6]].flat()
should produce [1,2,3,4,5,6] which is the same as:
[[1,2],[3,4,5],[6]].flatMap { $0 }
If you take a look at the flatMap(_:) signature,
extension Sequence {
// ...
public func flatMap<SegmentOfResult : Sequence>(_ transform: (Self.Iterator.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]
// ...
}
you'll see that it returns a [SegmentOfResult.Iterator.Element], where SegmentOfResult is the type that is returned from the function you pass it. This isn't necessarily the same type as Element (as your extension is for all arrays), which is why your code fails to compile.
In order to be working with arrays where the elements are sequences, you'll want to constrain your extension so that Element : Sequence.
Also, as the function you're passing to flatMap(_:) is an identity transform (it just returns the element it receives as input), you'll want to change the return type to [Element.Iterator.Element] (an array of the inner element).
extension Array where Element : Sequence {
func flat() -> [Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
Although that being said, I see no reason why this shouldn't be an extension of Sequence:
// An extension for a sequence of sequences
extension Sequence where Iterator.Element : Sequence {
// returns an array of the inner element type (an array of the element of the element)
func flat() -> [Iterator.Element.Iterator.Element] {
return self.flatMap { $0 }
}
}
(However, I don't really see the need to create an extension for this in the first place – array.flatMap{$0} isn't exactly lengthy!)
I have a Swift function that accepts Any and I want it to be able to accept an array of Strings, an array of Ints, a mixed array, or an array of arrays, etc. It also can accept just a String or an Int, etc, not in an array.
So I have this:
private func parse(parameter: Any) {
if parameter is Int {
// Int
} else if (parameter is Float) || (parameter is Double) {
// Double
} else if parameter is String {
// String
} else if parameter is Bool {
// Bool
} else if let array = parameter as? [Any] {
// Should catch all Arrays
} else {
assert(false, "Unsupported type") // [String] ends up here
}
}
But if I call parse(["Strings"]), the assert is raised. How can I catch all types of Arrays?
edit - there was some confusion as to what I'm trying to accomplish. I basically need to return a String based on the type, so Int -> "" and String -> "", so an array would make recursive calls to return "..."
This post is marked as a duplicate, but that other question is about Javascript, not Swift.
I finally found the way to do that, which is to use NSArray for casting.
private func parse(x: Any) {
if let o = x as? [Any] {
println("[Any]")
}
if let o = x as? [AnyObject] {
println("[AnyObject]")
}
if let o = x as? NSArray {
println("NSArray")
}
}
let a: [Any] = ["bar"]
let b: [AnyObject] = ["bar"]
let c = ["foo", 3.14]
parse(a) // ==> [Any]
parse(b) // ==> [AnyObject], and also NSArray
parse(c) // ==> NSArray
It look so that an array containing values of Any internally represented in NSArray.
(But should it be able to cast c to [Any]...? I'm suspecting it's a bug.)
The key to understanding typing and type related issues in Swift is that all roads lead to protocols.
The challenge of this problem is detecting any type of array, not just one concrete type. The OP's example failed because [Any] is not a base class or a generalized pattern of [String], that is to say, that (from what I can tell), in Swift [T] is not covariant on T. Beyond that, you cannot check for SequenceType or CollectionType since they have associated types (Generator.Element).
The idiomatic solution is thus to use a marker protocol to indicate which types you want to match your criteria. As illustrated below, you achieve this by creating an empty protocol, and associating it with the types of interest.
import Foundation
protocol NestedType {}
extension Array: NestedType {}
extension Set: NestedType {}
extension Dictionary: NestedType {}
extension NSSet: NestedType {}
protocol AnyTypeOfArray {}
extension Array: AnyTypeOfArray {}
extension NSArray: AnyTypeOfArray {}
protocol AnyTypeOfDictionary {}
extension Dictionary: AnyTypeOfDictionary {}
func printType(v:Any) {
if v is NestedType {
print("Detected a nested type")
}
if v is AnyTypeOfArray {
print("\t which is an array")
}
else if v is AnyTypeOfDictionary {
print("\t which is a dictionary")
}
}
printType([String:Int]())
printType([Int]())
printType(NSArray())
The output of which is:
Detected a nested type
which is a dictionary
Detected a nested type
which is an array
Detected a nested type
which is an array
One way you can do this is to separate the function out to two separate implementations (with the same name), one that takes anArray and one for everything else. You'll also need to make them generic functions instead of using the Any type. With that setup, Swift can use type inference to figure out the best function to call.
I'd implement it something like this (I'm just printlning the type to show where things end up):
func parse<T>(parameter: T) {
if parameter is Int {
println("Int")
} else if (parameter is Float) || (parameter is Double) {
println("Double")
} else if parameter is String {
println("String")
} else if parameter is Bool {
println("Bool")
} else {
assert(false, "Unsupported type")
}
}
func parse<T>(parameter: Array<T>) {
println("Array")
for element in parameter {
// Recursively parsing...
parse(element)
}
}
Then calling it like this:
parse(1) // Int
parse(0.1) // Double
parse("asdf") // String
parse(true) // Bool
parse(["asdf", "asdf"]) // Array -> String String
Outputs:
Int
Double
String
Bool
Array
String
String
You can use the _stdlib_getTypeName that returns the mangled type name for the given value.
For example:
var myString = "String"
var myInteger = 10
var myArray = [10,22]
var myDictionary = ["one": 1, "two": 2, "three": 3]
println("\(_stdlib_getTypeName(myString))")
println("\(_stdlib_getTypeName(myInteger))")
println("\(_stdlib_getTypeName(myArray))")
println("\(_stdlib_getTypeName(myDictionary))")
The result will be:
_TtSS // for String
_TtSi // for integer
_TtSa // for array
_TtVSs10Dictionary // for dictionary