I am using the following code in xCode 6.4 to split strings inside an array into arrays:
func getData() -> [String]
{
let data = navData
// navData is like:
// A|xxx|xxx|xxx
// R|ccc|ccc|ccc
// N|ccc|ccc|ccc
// N|ccc|ccc|ccc
return split(data) { $0 == "\n" }
}
let data:[String] = getData()
func search(query:(String, Int)) -> [String]
{
let recs:[String] = data.filter { $0.hasPrefix(query.0) }
var cols: [String] = recs.map { split( recs ) { $0 == "|" } }
}
func searchQueries() -> [(String, Int)]
{
return [("N", 1)] //key, column index
}
for q:(String, Int) in searchQueries()
{
var results:[String] = search(q)
for x in results
{
result = results[0]
}
}
It used to work before, but I guess swift was changed in 1.2 and it gives the following error now:
Cannot invoke 'map' with an argument list of type '(() -> _)'
Any suggestions?
Thank you!
After discovering that in Swift two you have to split strings by using its characters property, I made this work in playground:
let recs = ["col1|col2|col3", "1|2|3"]
let cols = recs.map {
split($0.characters) { $0 == "|" }.map {String($0)}
}
cols.first // ["col1", "col2", "col3"]
cols.last // ["1", "2", "3"]
Note that in Swift 2 beta 2 you can also use {String.init} at the end.
To make this work in Swift 1.2, remove .characters.
Related
I've got two function. They are doing the same thing actually. Getting data from database results by fieldNameArray into array of Int or Double.. how can i merge these two functions into one ? is that possible ? only difference is fields type and so return type..
func fieldsToInt ( fieldNameArray : [String] ) -> [Int] {
var returnArray = [Int]()
for fNA in fieldNameArray {
let n = Int ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
func fieldsToDouble ( fieldNameArray : [String] ) -> [Double] {
var returnArray = [Double]()
for fNA in fieldNameArray {
let n = Double ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
You can try generics, like
func fieldsToNumber<T> ( fieldNameArray : [String] ) -> [T] {
var returnArray = [T]()
for fNA in fieldNameArray {
let n = T ( results.int(forColumn: fNA) )
returnArray.append( n )
}
return returnArray;
}
Yes, it is possible, you should use Generics, in particular you need a generic function.
The code that solves your problem would roughly look like this:
// This is a struct to mock the code you didn't post
struct Results {
func int(forColumn: String) -> Int16 {
return 1
}
}
let results = Results()
protocol DatabaseElementRepresentable {
init?(_ databaseValue: Int16)
}
func fields<Element: DatabaseElementRepresentable>(fieldNameArray : [String]) -> [Element] {
var returnArray = [Element]()
for fNA in fieldNameArray {
if let n = Element(results.int(forColumn: fNA)) {
returnArray.append(n)
}
}
return returnArray
}
extension Int: DatabaseElementRepresentable { }
extension Double: DatabaseElementRepresentable { }
let ints: [Int] = fields(fieldNameArray: ["test"])
let doubles: [Double] = fields(fieldNameArray: ["test"])
Please, change Int16 with the value the function int(forColumn: returns
ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr
I would like to be able to find and replace occurrences of a substring in a native Swift string without bridging to the NS class. How can I accomplish this?
This is not a duplicate of this question, as that question is about replacing a single character. This question is about finding and replacing a substring, which may contain many characters.
Method without Foundation:
extension String {
func replacing(_ oldString: String, with newString: String) -> String {
guard !oldString.isEmpty, !newString.isEmpty else { return self }
let charArray = Array(self.characters)
let oldCharArray = Array(oldString.characters)
let newCharArray = Array(newString.characters)
var matchedChars = 0
var resultCharArray = [Character]()
for char in charArray {
if char == oldCharArray[matchedChars] {
matchedChars += 1
if matchedChars == oldCharArray.count {
resultCharArray.append(contentsOf: newCharArray)
matchedChars = 0
}
} else {
for i in 0 ..< matchedChars {
resultCharArray.append(oldCharArray[i])
}
if char == oldCharArray[0] {
matchedChars = 1
} else {
matchedChars = 0
resultCharArray.append(char)
}
}
}
return String(resultCharArray)
}
}
Example usage:
let myString = "Hello World HelHelloello Hello HellHellooo"
print(myString.replacing("Hello", with: "Hi"))
Output:
Hi World HelHiello Hi HellHioo
Method using Foundation:
You can use the replacingOccurrences method on the String struct.
let myString = "Hello World"
let newString = myString.replacingOccurrences(of: "World", with: "Everyone")
print(newString) // prints "Hello Everyone"
generic and pure Swift approach
func splitBy<T: RangeReplaceableCollection>(_ s:T, by:T)->[T] where T.Iterator.Element:Equatable {
var tmp = T()
var res = [T]()
var i:T.IndexDistance = 0
let count = by.count
var pc:T.Iterator.Element {
get {
i %= count
let idx = by.index(by.startIndex, offsetBy: i)
return by[idx]
}
}
for sc in s {
if sc != pc {
i = 0
if sc != pc {
} else {
i = i.advanced(by: 1)
}
} else {
i = i.advanced(by: 1)
}
tmp.append(sc)
if i == count {
tmp.removeSubrange(tmp.index(tmp.endIndex, offsetBy: -i)..<tmp.endIndex)
res.append(tmp)
tmp.removeAll()
}
}
res.append(tmp)
return res
}
func split(_ s:String, by:String)->[String] {
return splitBy(s.characters, by: by.characters).map(String.init)
}
extension RangeReplaceableCollection where Self.Iterator.Element: Equatable {
func split(by : Self)->[Self] {
return splitBy(self, by: by)
}
}
how to use it?
let str = "simple text where i would like to replace something with anything"
let pat = "something"
let rep = "anything"
let s0 = str.characters.split(by: pat.characters).map(String.init)
let res = s0.joined(separator: rep)
print(res) // simple text where i would like to replace anything with anything
let res2 = split(str, by: pat).joined(separator: rep)
print(res2) // simple text where i would like to replace anything with anything
let arr = [1,2,3,4,1,2,3,4,1,2,3]
let p = [4,1]
print(arr.split(by: p)) // [[1, 2, 3], [2, 3], [2, 3]]
How to sort array according to number of occurrence of string
Example :
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
and sorted array should be like this
["Me","Hello","That","as","the"]
Updated For Swift 3
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in array {
counts[item] = (counts[item] ?? 0) + 1
}
print(counts)
let result = counts.sorted { $0.value > $1.value }.map { $0.key }
print(result)
array.removeAll()
for string in result {
array.append(string)
}
print(array)
This is what I have been able to come up with:
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
// record the occurences of each item
var dict = [String: Int]()
for item in array {
if dict[item] == nil {
dict[item] = 1
} else {
dict[item]! += 1
}
}
// here I sort the dictionary by comparing the occurrences and map it so that the result contains only the key (the string)
let result = dict.sorted { $0.value > $1.value }.map { $0.key }
Try this -
It is tested and working as expected --
let arrayName = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in arrayName {
counts[item] = (counts[item] ?? 0) + 1
}
let array = counts.keysSortedByValue(isOrderedBefore: >)
print(array) // Output - ["Me", "Hello", "the", "That", "as"]
Create Dictionary extension -
extension Dictionary {
func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
return Array(self.keys).sorted(by: isOrderedBefore)
}
// Faster because of no lookups, may take more memory because of duplicating contents
func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return Array(self)
.sorted() {
let (_, lv) = $0
let (_, rv) = $1
return isOrderedBefore(lv, rv)
}
.map {
let (k, _) = $0
return k
}
}
}
It looks simple.
1. Take distinct from your array.
2. Make count according to distinct list.
3. Save results in collection - ie Dictionary.
4. Sort new collection.
Loop through the array and maintain a word count dictionary. Make sure the dictionary can be sorted based on values and finally obtain the set of keys and transform it back into an array.
This should work.
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var tR : [String : Int] = [:]
let finalResult = array.reduce(tR) { result, item in
var tArr : [String: Int] = result
if let count = tArr[item] {
tArr[item] = count+1
} else {
tArr[item] = 1
}
return tArr
}
.sorted(by: { item1, item2 in
return item1.value > item2.value
}).map() { $0.key }
Please try this, hope it helps
var terms = ["Hello","Me","That","Me","Hello","Me","as","the"]
var termFrequencies = [String: Int]()
for t in terms {
if termFrequencies[t] == nil {
termFrequencies[t] = 1
} else {
termFrequencies[t] = termFrequencies[t]! + 1
}
}
for value in terms {
let index = termFrequencies[value] ?? 0
termFrequencies[value] = index + 1
}
let result = termFrequencies.sorted{$0.1 > $1.1}.map{$0.0}
I'm trying to write a function literal in swift with a recursive body - in this case it's simply to add all the values in a list. I'm getting an error that "Variable used within it's own initial value". Any thoughts on what might be wrong here? Also I'm aware that what I'm doing here is a simple reduce and that it's build into Array, I'm just using this as an illustrative example of what I'm seeing elsewhere.
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure = { (memo: Int, list: Slice<Int>) -> Int in
if (list.count == 0) {
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0,list)
Try this:
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure:((Int, Slice<Int>) -> Int)!
closure = { (memo, list) in
if (list.count == 0) {
closure = nil // remove retain cycle
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0, list)
EDIT:
see this video: Advanced Swift at WWDC14. from around 41:00. it shows the down side of this method, and better workaround.
I know this is quite old, but I've found another alternative:
let list : ArraySlice<Int> = [1,2,3,4,5,6,7,8,9,10]
let closure = { (Void) -> ((Int, ArraySlice<Int>) -> Int) in
func f(memo: Int, list: ArraySlice<Int>) -> Int {
if (list.count == 0) {
return memo
} else {
return f(memo + list[list.startIndex], list: list[(list.startIndex + 1)..<list.endIndex])
}
}
return f
}()
let value = closure(0, list)