Create array of strings from a string that contains certain (dual) characters - arrays

I have a string of words and mathematical/programmatically used symbols. For example, something like this:
let source = "a + b + 3 == c"
(Note: you cannot rely on spaces)
I also have an array of strings that I need to filter out of the source string:
let symbols = ["+", "-", "==", "!="]
Now, I need to create an array of the matching items (with duplicates). In this case that would be ["+", "+", "=="].
From what I've tried, == is two characters, so I cannot do the following:
let source = "a + b + 3 == c"
let symbols = CharacterSet(charactersIn: "+-=≠") // not '==' and '!=', but '=' and '≠' due to it being a CharacterSet
let operations = source
.map { String($0) }
.filter { char in symbols.contains(UnicodeScalar(char)!) }
print(operations)
// Output: ["+", "+", "=", "="]
// Needed: ["+", "+", "=="]
Any help is much appreciated

What you need is to threat your symbols as regular strings. Iterate each symbol and search it in your source. If you find a range append it to a collection, get the substring at that location and search again your string from the index after the range upperBound. When you reach the end of the string searching fro that symbol remove the substrings from your source. Try something like this:
var source = "a + b + 3 == c"
let symbols = ["+", "-", "==", "!="]
var results: [Substring] = []
for symbol in symbols {
var startIndex = source.startIndex
var ranges: [Range<String.Index>] = []
while startIndex < source.endIndex,
let range = source[startIndex...].range(of: symbol) {
ranges.append(range)
results.append(source[range])
startIndex = source.index(after: range.upperBound)
}
for range in ranges.reversed() {
source.removeSubrange(range)
}
}
print(results)
this will print
["+", "+", "=="]

This problem is a parsing problem, which is to convert an input string into some kind of structured data. A nice way to do that is with parser combinators. Although the guts is complex, once set up, the example 'opStrings' function is simple to understand and it's easy to extend.
struct Parser<A> {
let run: (inout Substring) -> A?
static func always<A>(_ a: A) -> Parser<A> {
.init { _ in a }
}
static var never: Parser {
.init { _ in nil }
}
func map<B>(_ f: #escaping (A) -> B) -> Parser<B> {
Parser<B> { str -> B? in
self.run(&str).map(f)
}
}
func flatMap<B>(_ f: #escaping (A) -> Parser<B>) -> Parser<B> {
Parser<B> { str -> B? in
let original = str
let matchA = self.run(&str)
let parserB = matchA.map(f)
guard let matchB = parserB?.run(&str) else {
str = original
return nil
}
return matchB
}
}
func run(_ str: String) -> (match: A?, rest: Substring) {
var str = str[...]
let match = self.run(&str)
return (match, str)
}
}
func prefix(while p: #escaping (Character) -> Bool) -> Parser<Substring> {
Parser<Substring> { str in
let prefix = str.prefix(while: p)
str.removeFirst(prefix.count)
return prefix
}
}
func literalString(_ p: String) -> Parser<String?> {
Parser<String?> { str in
guard str.hasPrefix(p) else { return nil }
str.removeFirst(p.count)
return p
}
}
func literal(_ str: String) -> Parser<Void> {
literalString(str).map {
_ in ()
}
}
func oneOrMore<A>(_ predicate: #escaping (Character) -> Bool,
mapped f: #escaping (Substring) -> A) -> Parser<A> {
prefix(while: predicate)
.flatMap { $0.isEmpty ? .never : .always(f($0)) }
}
let oneOrMoreDecimals = oneOrMore((\.isNumber), mapped: Double.init)
.flatMap { d -> Parser<Double> in
guard let d = d else { return .never }
return .always(d)
}
func oneOf<A>(_ ps: [Parser<A>]) -> Parser<A> {
Parser<A> { str -> A? in
for p in ps {
if let match = p.run(&str) {
return match
}
}
return nil
}
}
let whitespace = oneOf([
literal(" "),
literal("\t"),
literal("\n")
])
let identifier = prefix(while: { ($0.isLetter && $0.isASCII) || $0.isNumber })
.flatMap { str -> Parser<String> in
guard let first = str.first else { return Parser.never }
return first.isNumber ? .never : .always(String(str))
}
let literalNumber = prefix(while: { $0.isNumber })
.flatMap { str -> Parser<Double> in
guard let value = Double(str) else { return Parser.never }
return .always(value)
}
func skip<T, U>(_ p: Parser<T>) -> Parser<U?> {
p.map { _ in nil }
}
struct Example {
enum Token {
case minus
case plus
case equal
case equalEqual
case notEqual
case identifier(String)
case number(Double)
}
enum Error: Swift.Error {
case unexpectedInput
}
let input: String
func run<T>(_ parsers: [Parser<T?>]) throws -> [T] {
var source = Substring(input)
var results: [T] = []
while let token = oneOf(parsers).run(&source) {
if let token = token {
results.append(token)
}
}
guard source.isEmpty else {
throw Error.unexpectedInput
}
return results
}
func tokenize() throws -> [Token] {
let tokenizers: [Parser<Token?>] = [
literal("-").map { .minus },
literal("+").map { .plus },
literal("==").map { .equalEqual },
literal("!=").map { .notEqual },
literal("=").map { .equal },
identifier.map { .identifier($0) },
oneOrMoreDecimals.map { .number($0) },
skip(whitespace)
]
return try run(tokenizers)
}
func opStrings() throws -> [String] {
try run([
literalString("-"),
literalString("+"),
literalString("=="),
literalString("!="),
literalString("="),
skip(identifier),
skip(oneOrMoreDecimals),
skip(whitespace),
] as [Parser<String?>])
}
}
do {
let example = Example(input: "a + b + 3 == c")
// let tokens = try example.tokenize()
print(try example.opStrings()) // ["+", "+", "=="]
}
catch {
print(error)
}

Related

Does Swift offer any built-in function to return the result of appending to an immutable array?

Writing the question and answer from here, I'm curious to know if there is any simpler way to write the following:
var nums = [1,2,3]
let sum1 = nums.reduce([Int]()){
let temp = $0
temp.append($1)
return temp
}
I know I can do:
var nums = [1,2,3]
let sum1 = nums.reduce([Int]()){
return $0 + [$1]
}
But that comes off as a hack.
To explain this better, I want to get closer to the example (from docs) below, just that it should be for an array:
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
x + y
})
EDIT:
Since folks asked what was I trying to achieve:
I was doing the leetcode's group Anagram's challenge.
My solution was:
struct WordTraits: Equatable{
let count: Int
let charactersSet: Set<Character>
}
struct Word: Equatable{
let string: String
let wordTraits: WordTraits
}
class Solution{
func groupAnagrams(_ strs: [String]) -> [[String]]{
var words : [Word] = []
var answers : [(traits: WordTraits, words: [Word])] = []
var count = 0
strs.forEach{ str in
count += 1
let count = str.count
let string = str
let characterSet = Set(str)
let wordTraits = WordTraits(count: count, charactersSet: characterSet)
let word = Word(string: string, wordTraits: wordTraits)
words.append(word)
}
while words.count != 0{
let word = words[0]
let traits = word.wordTraits
var isWordAdded = false
for (i, answer) in answers.enumerated(){
if answer.traits == traits{
answers[i].words.append(word)
isWordAdded = true
break
}
}
if !isWordAdded{
answers.append((traits: traits, words:[word]))
}
words.removeFirst()
}
let emptyArray : [[String]] = []
let finalAnswer = answers.reduce(emptyArray, { total, answer in
let strings : [String] = answer.words.reduce([String](), {
return $0 + [$1.string]
})
return total + [strings]
})
return finalAnswer
}
}
let s = Solution()
print(s.groupAnagrams(["ate", "eta", "beta", "abet"])) // [["ate", "eta"], ["beta", "abet"]]
reduce(..) has to know which type it is working with. To infer this it can use the return type or the type of the first argument. So you can also write:
var nums = [1,2,3]
let sum1: [Int] = nums.reduce([]){
return $0 + [$1]
}
[$1] can't be replaced with $1 because +-operator between value and collection is undefined.
Nope. But you can add it:
extension Array {
func appending(_ newElement: Element) -> Array<Element> {
return self + [newElement]
}
func appending(contentsOf sequence: Sequence) -> Array<Element> {
return self + sequence
}
}
Um, how about the + operator?
let nums = [1, 3, 5]
let more = nums + [7]
Your code is trying to convert a complex structure to an array of arrays. You can use map for this.
This should work:
let finalAnswer = answers.map { answer in
answer.words.map {
$0.string
}
}
Edit:
I was able to solve it using minimal code:
class Solution {
func groupAnagrams(_ words: [String]) -> [[String]] {
let processedWords = words.map {
(key: String($0.sorted()), value: $0)
}
return Dictionary(grouping: processedWords, by: { $0.key }).map { groupedValue in
groupedValue.value.map {
$0.value
}
}
}
}
You've greatly overcomplicated your computation of "final answers". It could just be:
return answers.map { $0.words.map { $0.string } }

Appending Array Inside Function

I am trying to append values to an array inside a function and call that updated array in another function but it is not printing the new appended values.
I am using firebase and getting a snapshot of the database, sorting it, finding value of keys, and appending to hDates
var hDates:Array = [""]
getHistoryPGE() { hDates in
print(hDates)
self.hDates = [hDates]
}
func getHistoryPGE(completion: #escaping (String)->()) {
let userID = Auth.auth().currentUser?.uid
let ref = Database.database().reference().child("users").child(userID!)
ref.child("PostGameEval").observe(.value, with: { (snapshot) in
if let dict = snapshot.value as? [String : [String: [String: Any]]] {
let keys = Array(dict.keys)
var num : Int = 7
for i in 0..<keys.count {
if let innerDict = dict[keys[i]] {
let innerKeys = Array((innerDict).keys)
let sortedInnerKeys = innerKeys.sorted(by: { $0 > $1} )
while (sortedInnerKeys.count < num){
num -= 1
}
for j in 0..<num {
if let tempDict = innerDict[sortedInnerKeys[j]] {
print(tempDict["1abA"] as! String)
}
}
}
}
}})
}
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter.string(from: date)
if self.hDates.contains(dateString) {
return 1
}
return 0
}

Does Map method of Swift Array implemented concurrently?

Checked the doc, but not talks about many details of implementation. Wondering if for a large array, does it perform in concurrent way?
The map implementation of Array specifically doesn't perform any multithreading, but there's nothing that says you couldn't make a concurrent implementation. Here's one that's generalized to work with any Sequence:
import Dispatch
class SharedSynchronizedArray<T> {
var array = [T]()
let operationQueue = DispatchQueue(label: "SharedSynchronizedArray")
func append(_ newElement: T) {
operationQueue.sync {
array.append(newElement)
}
}
}
public struct ConcurrentSequence<Base, Element>: Sequence
where Base: Sequence, Element == Base.Iterator.Element {
let base: Base
public func makeIterator() -> Base.Iterator {
return base.makeIterator()
}
public func map<T>(_ transform: #escaping (Element) -> T) -> [T] {
let group = DispatchGroup()
let resultsStorageQueue = DispatchQueue(label: "resultStorageQueue")
let results = SharedSynchronizedArray<T>()
let processingQueue = DispatchQueue(
label: "processingQueue",
attributes: [.concurrent]
)
for element in self {
group.enter()
print("Entered DispatchGroup for \(element)")
var result: T?
let workItem = DispatchWorkItem{ result = transform(element) }
processingQueue.async(execute: workItem)
resultsStorageQueue.async {
workItem.wait()
guard let unwrappedResult = result else {
fatalError("The work item was completed, but the result wasn't set!")
}
results.append(unwrappedResult)
group.leave()
print("Exited DispatchGroup for \(element)")
}
}
print("Started Waiting on DispatchGroup")
group.wait()
print("DispatchGroup done")
return results.array
}
}
public extension Sequence {
public var parallel: ConcurrentSequence<Self, Iterator.Element> {
return ConcurrentSequence(base: self)
}
}
print("Start")
import Foundation
let input = Array(0..<100)
let output: [Int] = input.parallel.map {
let randomDuration = TimeInterval(Float(arc4random()) / Float(UInt32.max))
Thread.sleep(forTimeInterval: randomDuration)
print("Transforming \($0)")
return $0 * 2
}
print(output)
// print(output.parallel.filter{ $0 % 3 == 0 })
print("Done")

Function Array<Optional<T>> -> Optional<Array<T>>

Here is what I'm trying to do:
extension Array<Optional<T>> {
func unwrap() -> Optional<Array<T>> {
let a = self.flatMap() { a in
switch a {
case Optional.Some(let x): return [x]
case Optional.None: return []
}
}
if a.count == self.count {
return Optional.Some(a)
} else {
return Optional.None
}
}
}
But, it doesn't compiles with error Use of undeclared type T.
Here is how I want to use it:
let a = [Optional.Some(2), Optional.Some(3), Optional.Some(4)]
let b = [Optional.Some(1), Optional.None]
a.unwrap() // Optional[2, 3, 4]
b.unwrap() // nil
How can I get around with this? Or there is no such possibility in swift?
Try this:
protocol OptionalType {
typealias W
var optional: W? { get }
}
extension Optional: OptionalType {
typealias W = Wrapped
var optional: W? { return self }
}
extension Array where Element: OptionalType {
func unwrap() -> [Element.W]? {
return reduce(Optional<[Element.W]>([])) { acc, e in
acc.flatMap { a in e.optional.map { a + [$0] } }
}
}
}
And then,
let a: [Int?] = [1, 2, 3]
let b: [Int?] = [1, nil, 3]
a.unwrap() // ==> [1, 2, 3]
b.unwrap() // ==> nil
Swift 4
Inspired by the solution by #findall, this works with Swift 4:
protocol OptionalType {
associatedtype Wrapped
var optional: Wrapped? { get }
}
extension Optional: OptionalType {
var optional: Wrapped? { return self }
}
extension Sequence where Iterator.Element: OptionalType {
func removeNils() -> [Iterator.Element.Wrapped] {
return self.flatMap { $0.optional }
}
}
Test:
class UtilitiesTests: XCTestCase {
func testRemoveNils() {
let optionalString: String? = nil
let strings: [String?] = ["Foo", optionalString, "Bar", optionalString, "Baz"]
XCTAssert(strings.count == 5)
XCTAssert(strings.removeNils().count == 3)
let integers: [Int?] = [2, nil, 4, nil, nil, 5]
XCTAssert(integers.count == 6)
XCTAssert(integers.removeNils().count == 3)
}
}
findall's solution works, although I think it's more readable to just avoid generics for this case:
func unwrap<Element>(optionalArray : [Element?]) -> [Element]? {
let unwrappedArray = optionalArray.flatMap { (a) -> [Element] in
switch a {
case Optional.Some(let x): return [x]
case Optional.None: return []
}
}
return unwrappedArray.count == optionalArray.count ? Optional.Some(unwrappedArray) : Optional.None
}
Usage:
let a = [Optional.Some(2), Optional.Some(3), Optional.Some(4)]
let b = [Optional.Some(1), Optional.None]
// Both are [Int]?
let unwrappedA = unwrap(a) // [2, 3, 4]
let unwrappedB = unwrap(b) // nil
See also: How to determine if a generic is an optional in Swift?

outside array not affected from function appending within

I am working with arrays and I created a function that appends an array from within. However when I print the array, it still appears empty. What gives?
var queriesFinal : [String] = []
func queryValidator(search : String)
{
var letterSet = NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz ")
var numberSet = NSCharacterSet(charactersInString: "1234567890".uppercaseString)
var queriesTwo : [String] = search.lowercaseString.componentsSeparatedByCharactersInSet(letterSet)
for(var x = 0; x < queriesTwo.count; x++)
{
for(var y = 0; y < 10; y++)
{
var str = String(y)
if(queriesTwo[x] == str)
{
var numberStr = String(queriesTwo[x]) + "th"
queriesFinal.append(numberStr)
}
}
}
}
println(queriesFinal)
search = "Matt 8th"
queryValidator(search)
This code can run in playground..
I appreciate any help!
As mentioned by Mike S, you've made a small mistake println should be after your queryValidator, I've also added an optional in case your queryValidator search returns nil, also as mentioned by Zaph you don't need numberSet, so I removed it:
func queryValidator(search : String) -> [String]? {
let queriesTwo:[String] = search.lowercaseString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefgjhijklmnopqrstuvwxyz "))
var queriesResult:[String] = []
for x in 0...queriesTwo.count-1 {
for y in 0...9 {
if(queriesTwo[x] == String(y)) {
queriesResult.append(String(queriesTwo[x]) + "th")
}
}
}
return queriesResult.count > 0 ? queriesResult : nil
}
var search = "Matt 8 less7"
if let queriesFinal = queryValidator(search) {
println(queriesFinal)
} else {
println("no result")
}
An alternative approach with Regular Expressions:
func queryValidator(search: String) -> [String] {
var queriesFinal:[String] = []
var nsSearch: NSString = search
let pattern = "(\\d+)"
var regEx = NSRegularExpression(pattern:pattern, options:nil, error:nil)
regEx?.enumerateMatchesInString(nsSearch, options:nil, range:NSMakeRange(0, nsSearch.length), usingBlock: { (result, flags, stop) in
let found = nsSearch.substringWithRange(result.range)
queriesFinal.append("\(found)th")
})
return queriesFinal
}
var result = queryValidator(search)
println("result: \(result)")
Output:
result: [8th, 7th, 6th]
For information on regular expressions see: Regular Expressions

Resources