Swift Pattern Match on Array<Any>

Swift switch pattern matching with arrays

You could define a custom pattern matching operator
~= which takes an array as the "pattern" and a value:

func ~=<T : Equatable>(array: [T], value: T) -> Bool {
return array.contains(value)
}

let foo = [1, 2, 3]
let bar = [4, 5, 6]

let value = 5

switch value {
case foo:
print("\(value) is in foo")
case bar:
print("\(value) is in bar")
default:
break
}

Similar operators exist already e.g. for intervals:

public func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool

Swift Pattern match on ArrayAny

Unfortunately casting between generic types like Array is not fully supported (yet). There are also odd situations even if you want to upcast:

let emptyStringArray : [String] = []
emptyStringArray as [Any] // succeeds

let stringArray : [String] = ["Bob", "Roger"]
stringArray as [Any] // error! due to the elements?!

let intArray = [1, 2, 3]
intArray as [Any] // error

let customStructArray : [myStruct] = []
customStructArray as [Any] // '[myStruct]' is not convertible to '[Any]'

There is also no good workaround without using a protocol. If you really want to have this dynamic behavior you could use reflections with the reflect() function. In Swift 2 they are more powerful, but it is still not a good solution.

Edit:

A solution with a protocol which gets adopted by all Arrays through an extension (only for your specific case):

protocol ArrayType {
var anyValues: [Any] { get }
}

extension Array: ArrayType {
var anyValues: [Any] {
return self.map { $0 as Any }
}
}

// now the switch gets rewritten as
switch any {
case let array as ArrayType:
let anyArray = array.anyValues
return "Array"
case let array as NSArray:
return "NSArray"
default:
return "Default"
}

Pattern matching in a Swift for loop

You can combine a pattern matching conditional cast with a where clause like so:

let myStuff: [AnyObject] = [5, "dog", 11, 15, "cat"]

// item will be an Int, and divisible by 5
for case let item as Int in myStuff where item % 5 == 0 {
print(item)
}

// Prints:
// 5
// 15

How to use Pattern matching in a Swift for loop for nested array

Not really pattern matching but without a loop

arr2.compactMap{$0.first{$0 is String}}.forEach{print($0)}

compactMap is necessary because first returns an optional. The position of the String in the array is irrelevant.

Is there a way to compare array with array using switch case in Swift?

You can use cases with where predicates:

let array = ["A", "B"]

switch array {
case _ where array == ["A", "B"]: print("AB")
case _ where array == ["C", "D"]: print("CD")
default: print("default")
}

If you really wanted, you could define a pattern match operator (~=) that calls ==. The switch statement looks for definitions of the pattern match operator that accept the given pattern and candidate to determine whether a case is matched:

let array = ["A", "B"]

func ~= <T: Equatable>(pattern: [T], candidate: [T]) -> Bool {
return pattern == candidate
}

switch array {
case ["A", "B"]: print("AB")
case ["C", "D"]: print("CD")
default: print("default")
}

I would advise against this, however, because it's not clear whether such a case is doing a == check, contains(_:), hasPrefix(_:), etc.

Swift - Search for pattern of numbers in an array

I still feel your question not entirely clear. Does the array [2,3,2,3,2] has 2 repeating subsequences of [2,3,2] even though they share an element in common?

If you want to avoid the "Can't form Range..." error, use stride:

// A more convenient way to check if two arrays are equal
func ==(lhs: [Int], rhs: [Int]) -> Bool {
guard lhs.count == rhs.count else { return false }

for (l, r) in zip(lhs, rhs) {
if l != r { return false }
}
return true
}

// If there's no matching pattern, this function will return nil
// instead of an aempty array. Change it if you want to
func hasPattern(array: [Int], length: Int) -> [[Int]]? {
guard array.count >= length * 2 else { return nil }

var result = [[Int]]()
for i in 0..<(array.count - length) {
let subarray1 = array[i..<(i+length)]

for j in stride(from: i+1, to: array.count - length, by: 1) {
let subarray2 = array[j..<(j+length)]
if subarray1 == subarray2 {
result.append(Array(subarray1))
}
}
}
return result.isEmpty ? nil : result
}

if let patterns = hasPattern(array: array5, length: 3) {
print(patterns) // [[39, 78, 324], [78, 324, 43]]
} else {
print("No repeating pattern found")
}

Retrieve array of substring matched with regex in swift

Your fundamental issue, as @jnpdx hinted at in a comment, is that your regexp string contains control elements from another language. The following should solve your issue:

let regexp = "@\\w*"

You also get bogged down in unnecessary try-catch statements and outdated APIs based on Objective-C and their related type conversions. The following should do:

func matches(for regex: String, in text: String) -> [String] {
var result = [String]()
var startIndex = text.startIndex
let endIndex = text.endIndex
while let range = text.range(of: regex,
options: .regularExpression,
range: startIndex ..< endIndex)
{
result.append(String(text[range]))
startIndex = range.upperBound
}
return result
}


Related Topics



Leave a reply



Submit