How to split string into Int:String Dictionary - swift

So I'm trying to split a string that would look like this:
let Ingredients = "1:egg,4:cheese,2:flour,50:sugar"
and I'm attempting to get a dictionary output like this
var decipheredIngredients : [Int:String] = [
1 : "egg",
4 : "cheese",
2 : "flour",
50 : "sugar"
]
Here is the code that I am attempting this with
func decipherIngredients(input: String) -> [String:Int]{
let splitStringArray = input.split(separator: ",")
var decipheredIngredients : [String:Int] = [:]
for _ in splitStringArray {
decipheredIngredients.append(splitStringArray.split(separator: ":"))
}
return decipheredIngredients
}
When I try this I get an error saying I can't append to the dictionary. I've tried other methods like this:
func decipherIngredients(input: String) -> [String.SubSequence]{
let splitStringArray = input.split(separator: ",")
return splitStringArray
}
let newThing = decipherIngredients(input: "1:egg,4:cheese,2:flour,50:sugar").split(separator: ":")
print(newThing)
but I get this as the output of the function
[ArraySlice(["1:egg", "4:cheese", "2:flour", "50:sugar"])]

An alternative approach using Swift 4 and functional programming:
let ingredients = "1:egg,4:cheese,2:flour,50:sugar"
let decipheredIngredients = ingredients.split(separator: ",").reduce(into: [Int: String]()) {
let ingredient = $1.split(separator: ":")
if let first = ingredient.first, let key = Int(first), let value = ingredient.last {
$0[key] = String(value)
}
}
print(decipheredIngredients)

Swift 3
try this, assuming you want dictionary keys of type Int and values of type String
func decipherIngredients(_ input: String) -> [Int:String] {
var decipheredIngredients : [Int:String] = [:]
let keyValueArray = input.components(separatedBy: ",")
for keyValue in keyValueArray {
let components = keyValue.components(separatedBy: ":")
decipheredIngredients[Int(components[0])!] = components[1]
}
return decipheredIngredients
}

Related

Swift: How to dynamically append value(s) into string of dictionary from an array of Int

Let say I have array of Int:
var intArr = [1,2,3,4]
and a string of dictionary:
var dictString = "[{\"id\": value1, {\"id\":value2,}, {\"id\":value3}, {\"id\":value4}]"
those value1,2,3,4 are Int type aswell
and I want to replace value1,2,3,4 with the value from stringArr. My expectation is something like
"[{\"id\": 1, {\"id\":2,}, {\"id\":3}, {\"id\":4}]"
Thank you!
The simplest solution here is to ignore the dictString and instead created an array of dictionaries using the intArray and then encode it and convert the result to a string
var intArr = [1,2,3,4]
let output = intArr.reduce(into: []) { $0.append(["id": $1])}
let data = try JSONEncoder().encode(output)
let result = String(data: data, encoding: .utf8)!
May be this you can achieve by one of the fowling way
Note: I'm assuming that you have below input
var intArr = [1,2,3,4]
var dictString = "[{\"id\": 0}, {\"id\":0}, {\"id\":0}, {\"id\":0}]"
Method 1:
Write an extension for String
extension String {
func getMapped(with list: [Int]) -> String? {
guard let jsonData = self.data(using: .utf8) else {
return nil
}
do{
var counter = -1
if let json = try JSONSerialization.jsonObject(with: jsonData) as? [[String: Int]]{
let result = json.map { ob -> [String: Int] in
counter += 1
return counter < list.count ? ["id" : list[counter]] : ob
}
return String(describing: result)
}
}catch let e {
print("e \(e)")
return nil
}
return nil
}
}
Now you can call that function
print(dictString.getMapped(with: intArr))
Output: Optional("[[\"id\": 1], [\"id\": 2], [\"id\": 3], [\"id\": 4]]")
Method 2:
Since I can see the id is in common for all dictionary objects, so you can simply map your intArry to convert to array of dictionary objects.
let mapped = intArr.map({["id": $0]})
let mappedString = String(describing: mapped)
print(mappedString)
Output: [["id": 1], ["id": 2], ["id": 3], ["id": 4]]
Edit: Editing my answer for method2 approach to address the #Larme's comment.
let mapped = intArr.map({["id": $0]})
let mappedString = String(describing: NSArray(array: mapped))
print(mappedString)
Output:
(
{
id = 1;
},
{
id = 2;
},
{
id = 3;
},
{
id = 4;
}
)

Find the repeated sequence in the line that go in a row

Given a string of arbitrary length. I need to find 1 subsequences of identical characters that go in a row.
My function (there are two of them, but these are two parts of the same function) turned out to be complex and cumbersome and did not fit because of this. The function I need should be simple and not too long.
Example:
Input : str = "abcabc"
Output : abc
Input : str = "aa"
Output : a
Input : str = "abcbabcb"
Output : abcb
Input : str = "abcbca"
Output : bcbc
Input : str = "cbabc"
Output :
Input : str = "acbabc"
Output :
My unsuccessful function:
func findRepetition(_ p: String) -> [String:Int] {
var repDict: [String:Int] = [:]
var p = p
while p.count != 0 {
for i in 0...p.count-1 {
repDict[String(Array(p)[0..<i]), default: 0] += 1
}
p = String(p.dropFirst())
}
return repDict
}
var correctWords = [String]()
var wrongWords = [String]()
func getRepeats(_ p: String) -> Bool {
let p = p
var a = findRepetition(p)
for i in a {
var substring = String(Array(repeating: i.key, count: 2).joined())
if p.contains(substring) {
wrongWords.append(p)
return false
}
}
correctWords.append(p)
return true
}
I will be very grateful for your help!
Here's a solution using regular expression. I used a capture group that tries to match as many characters as possible such that the whole group repeats at least once.
import Foundation
func findRepetition(_ s: String) -> String? {
if s.isEmpty { return nil }
let pattern = "([a-z]+)\\1+"
let regex = try? NSRegularExpression(pattern: pattern, options: [])
if let match = regex?.firstMatch(in: s, options: [], range:
NSRange(location: 0, length: s.utf16.count)) {
let unitRange = match.range(at: 1)
return (s as NSString).substring(with: unitRange)
}
return nil
}
print(findRepetition("abcabc")) //prints abc
print(findRepetition("aa")) //prints a
print(findRepetition("abcbabcb")) //prints abcb
print(findRepetition("abcbca")) //prints bc
print(findRepetition("cbabc")) //prints nil
print(findRepetition("acbabc")) //prints nil
func findRepetitions(_ p : String) -> [String: Int]{
let half = p.count / 2 + 1
var result : [String : Int] = [:]
for i in 1..<half {
for j in 0...(p.count-i) {
let sub = (p as! NSString).substring(with: NSRange.init(location: j, length: i))
if let val = result[sub] {
result[sub] = val + 1
}else {
result[sub] = 1
}
}
}
return result
}
This is for finding repetitions of possible substrings in your string. Hope it can help
Here is a solution that is based on the Suffix Array Algorithm, that finds the longest substring that is repeated (contiguously):
func longestRepeatedSubstring(_ str: String) -> String {
let sortedSuffixIndices = str.indices.sorted { str[$0...] < str[$1...] }
let lcsArray = [0]
+
sortedSuffixIndices.indices.dropFirst().map { index in
let suffix1 = str[sortedSuffixIndices[index]...]
let suffix2 = str[sortedSuffixIndices[index - 1]...]
let commonPrefix = suffix1.commonPrefix(with: suffix2)
let count = commonPrefix.count
let repeated = suffix1.dropFirst(count).commonPrefix(with: commonPrefix)
return count == repeated.count ? count : 0
}
let maxRepeated = zip(sortedSuffixIndices.indices,lcsArray).max(by: { $0.1 < $1.1 })
if let tuple = maxRepeated, tuple.1 != 0 {
let suffix1 = str[sortedSuffixIndices[tuple.0 - 1]...]
let suffix2 = str[sortedSuffixIndices[tuple.0]...]
let longestRepeatedSubstring = suffix1.commonPrefix(with: suffix2)
return longestRepeatedSubstring
} else {
return ""
}
}
Here is an easy to understand tutorial about such an algorithm.
It works for these examples:
longestRepeatedSubstring("abcabc") //"abc"
longestRepeatedSubstring("aa") //"a"
longestRepeatedSubstring("abcbabcb") //"abcd"
longestRepeatedSubstring("abcbca") //"bcbc"
longestRepeatedSubstring("cbabc") //""
longestRepeatedSubstring("acbabc") //""
As well as these:
longestRepeatedSubstring("a😍ca😍c") //"a😍c"
longestRepeatedSubstring("Ab cdAb cd") //"Ab cd"
longestRepeatedSubstring("aabcbc") //"bc"
Benchmarks
Here is a benchmark that clearly shows that the Suffix Array algorithm is much faster than using a regular expression.
The result is:
Regular expression: 7.2 ms
Suffix Array : 0.1 ms

Swift: Filter a Dictionary with Array as Value

I'm new to Swift programming. For my particular project, I'm trying to filter a dictionary with some user input, and the dictionary's value consists of an array.
Here is some sample code, and what I'm trying to accomplish:
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var filteredDictionary = [String: [String]]()
var searchText = "aberration"
//getting the first letter of string
var firstLetter = searchText[searchText.startIndex]
With this particular searchText, I'm trying to get:
filteredDictionary = ["a": ["aberration"]]
Edit: I want the dictionary to return with the first letter as its key, and the values with what searchText matches up with. Sorry if it I wasn't clear.
Here is some code I have tried, but obviously, I can't get it to work:
filteredDictionary = dictionary.filter{$0.key == firstLetter && for element in $0.value { element.hasPrefix(searchText) }}
Any help would be appreciated. Thanks.
Here's a solution that maps the values based on the search and then filters out the empty results.
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var searchText = "aberration"
let filteredDictionary = dictionary.mapValues { $0.filter { $0.hasPrefix(searchText) } }.filter { !$0.value.isEmpty }
print(filteredDictionary)
Output:
["a": ["aberration"]]
Try this:
var dictionary = ["a": ["aberration", "abc"], "b" : ["babel", "bereft"]]
var searchText = "aberration"
var filteredDictionary = dictionary.filter { (key, value) -> Bool in
return (value as! [String]).contains(searchText)
}.mapValues { (values) -> [String] in
return [searchText]
}
print(filteredDictionary)
You can use a combination of filter and map to achieve the desired result.
Output:
["a": ["aberration"]]
let firstLetter = String(searchText[searchText.startIndex])
let filteredDictionary = dictionary
.reduce(into: [String: [String]]()) { (result, object) in
if object.key == firstLetter {
let array = object.value.filter({ $0.hasPrefix(searchText) })
if array.count > 0 {
result[object.key] = array
}
}
}
Output:
["a": ["aberration"]]

Create a Dictionary from optionals

I some optionals: numberOfApples:Int?, numberOfBananas:Int?, numberOfOlives:Int? and I'd like to create a dictionary of just the set values. Is there way do succinctly create this?
The closest I've got is:
// These variables are hard-coded for the example's
// sake. Assume they're not known until runtime.
let numberOfApples: Int? = 2
let numberOfBananas: Int? = nil
let numberOfOlives: Int? = 5
let dict: [String:Int?] = ["Apples" : numberOfApples,
"Bananas" : numberOfBananas,
"Olives" : numberOfOlives]
And I'd like to dict to be of type: [String:Int] like so:
["Apples" : 2,
"Olives" : 5]
But this gives me a dictionary of optionals and accessing a value by subscripting gives my a double-wrapped-optional.
I realise that I could do this with a for-loop, but I was wondering if there's something more elegant.
Many thanks in advance.
Personally I would do it this way (and it's how I personally do this when it comes up):
var dict: [String: Int] = [:]
dict["Apples"] = numberOfApples
dict["Bananas"] = numberOfBananas
dict["Olives"] = numberOfOlives
Simple. Clear. No tricks.
But if you wanted to, you could write Dictionary.flatMapValues (to continue the pattern of Dictionary.mapValues). It's not hard. (EDIT: Added flattenValues() to more closely match original question.)
extension Dictionary {
func flatMapValues<T>(_ transform: (Value) throws -> T?) rethrows -> [Key: T] {
var result: [Key: T] = [:]
for (key, value) in self {
if let transformed = try transform(value) {
result[key] = transformed
}
}
return result
}
func flattenValues<U>() -> [Key: U] where Value == U? {
return flatMapValues { $0 }
}
}
With that, you could do it this way, and that would be fine:
let dict = [
"Apples" : numberOfApples,
"Bananas": numberOfBananas,
"Olives" : numberOfOlives
].flattenValues()
You can use filter and mapValues. You first filter all pairs where the value is not nil and then you can safely force unwrap the value. This will change the dict type to [String: Int].
let dict = [
"Apples": numberOfApples,
"Bananas": numberOfBananas,
"Olives": numberOfOlives
]
.filter({ $0.value != nil })
.mapValues({ $0! })
print(dict) //["Olives": 5, "Apples": 2]
Try this:
let numberOfApples: Int? = 5
let numberOfBananas: Int? = nil
let numberOfOlives: Int? = 5
let dict: [String: Int?] = [
"Apples": numberOfApples,
"Bananas": numberOfBananas,
"Olives": numberOfOlives
]
extension Dictionary {
func flatMapValues<U>() -> [Key: U] where Value == Optional<U> {
return reduce(into: [:]) { $0[$1.key] = $1.value }
// Keeping this line as it provides context for comments to this answer. You should delete it if you copy paste this.
// return filter { $0.value != nil } as! [Key : U]
}
}
let unwrappedDict = dict.flatMapValues()
let foo: Int?? = dict["Apples"]
let bar: Int? = unwrappedDict["Apples"]

Array of structs: UserDefaults, how to use?

I've already check all of those topics:
How to save an array of custom struct to NSUserDefault with swift?
How to save struct to NSUserDefaults in Swift 2.0
STRUCT Array To UserDefaults
I have a struct containing some Strings and an other struct: MySection.
struct MySection {
var name: String = ""
var values: [MyRow] = []
}
And there is MyRow which is store in MySection.values
struct MyRow {
var value: String = ""
var quantity: String = ""
var quantityType: String = ""
var done: String = ""
}
Two arrays for use it
var arraySection: [MySection] = []
var arrayRow: [MyRow] = []
And in my application, I add dynamically some values in those arrays.
There is the delegate method for get datas from my second ViewController
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow())
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
And there is the manageSection function.
func manageSection(item: String) {
var i = 0
for _ in arraySection {
if arraySection[i].name == item {
arraySection.insert(MySection(), at: i + 1)
arraySection[i + 1].values = [arrayRow[arrayRow.count - 1]]
return
}
i += 1
}
arraySection.append(MySection())
arraySection[arraySection.count - 1].name = item
arraySection[arraySection.count - 1].values = [arrayRow[arrayRow.count - 1]]
}
My need is to store datas of the two arrays in UserDefaults (or CoreData maybe??) and use these datas when the user going back to the application.
I don't know how to do it, I've already try methods from the 3 topics but I'm not even doing a good job.
How can I do it?
Thanks guys!
Since both types contain only property list compliant types a suitable solution is to add code to convert each type to a property list compliant object and vice versa.
struct MySection {
var name: String
var values = [MyRow]()
init(name : String, values : [MyRow] = []) {
self.name = name
self.values = values
}
init(propertyList: [String: Any]) {
self.name = propertyList["name"] as! String
self.values = (propertyList["values"] as! [[String:String]]).map{ MyRow(propertyList: $0) }
}
var propertyListRepresentation : [String: Any] {
return ["name" : name, "values" : values.map { $0.propertyListRepresentation }]
}
}
struct MyRow {
var value: String
var quantity: String
var quantityType: String
var done: String
init(value : String, quantity: String, quantityType: String, done: String) {
self.value = value
self.quantity = quantity
self.quantityType = quantityType
self.done = done
}
init(propertyList: [String:String]) {
self.value = propertyList["value"]!
self.quantity = propertyList["quantity"]!
self.quantityType = propertyList["quantityType"]!
self.done = propertyList["done"]!
}
var propertyListRepresentation : [String: Any] {
return ["value" : value, "quantity" : quantity, "quantityType" : quantityType, "done" : done ]
}
}
After creating a few objects
let row1 = MyRow(value: "Foo", quantity: "10", quantityType: "Foo", done: "Yes")
let row2 = MyRow(value: "Bar", quantity: "10", quantityType: "Bar", done: "No")
let section = MySection(name: "Baz", values: [row1, row2])
call propertyListRepresentation to get a dictionary ([String:Any]) which can be saved to User Defaults.
let propertyList = section.propertyListRepresentation
Recreation of the section is quite easy, too
let newSection = MySection(propertyList: propertyList)
Edit
Use the propertyList initializer only if you get data from UserDefaults in all other cases use the other initializer.
For example replace
#IBAction func addButtonPressed(_ sender: Any) {
newProducts.append(MyRow(propertyList: ["":""]))
newProducts[newProducts.count - 1].value = nameTextField.text!
newProducts[newProducts.count - 1].quantity = quantityTextField.text!
newProducts[newProducts.count - 1].quantityType = type
newProducts[newProducts.count - 1].done = "No"
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
with
#IBAction func addButtonPressed(_ sender: Any) {
let row = MyRow(value: nameTextField.text!,
quantity: quantityTextField.text!,
quantityType: type,
done: "No")
newProducts.append(row)
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
and replace
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow(propertyList: ["":""]))
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
with
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(newItem[0])
manageSection(item: sectionPick)
listTableView.reloadData()
}
Basically first create the object, then append it to the array. The other way round is very cumbersome.