Given two separate arrays, I would like to combine those into a single array of dictionaries.
For example, given:
var array = ["myDataOne", "myDataTwo", "myDataThree", "myDataFour"]
var array_second = ["apple", "banana", "orange", "kiwi"]
And I would like that my array to look like:
dictionnary[0] -> dataOne: "myDataOne", dataSecond: "apple"
dictionnary[1] -> dataOne: "myDataTwo", dataSecond: "banana"
dictionnary[2] -> dataOne: "myDataThree", dataSecond: "orange"
dictionnary[3] -> dataOne: "myDataFour", dataSecond: "kiwi"
I'm coding in Swift 2 using Xcode 7 B5.
EDIT : Just to clarify, in fact the final array should look like this:
[[dataOne: "myDataOne", dataSecond: "apple"],[dataOne: "myDataTwo", dataSecond: "banana"],[dataOne: "myDataThree", dataSecond: "orange"],[dataOne: "myDataFour", dataSecond: "kiwi"]]
So array[0] must return something like [dataOne: "myDataOne", dataSecond: "apple"] and if i do array[0].dataOne it must return "myDataOne".
The typical procedural approach would be:
var result = [[String: String]]()
for var i = 0; i < array.count; i++ {
result.append(["dataOne": array[i], "dataSecond" : array_second[i]])
}
--
You can also do this functionally:
let result = zip(array, array_second).map { ["dataOne" : $0.0, "dataSecond" : $0.1] }
Related
I've two cases where I need to convert strings to different formats.
for ex:
case 1:
string inputs: abc, xyz, mno, & llr // All Strings from a dictionary
output: ["abc","xyz", "mno", "llr"] //I need to get the String array like this.
But when I use this code:
var stringBuilder:[String] = [];
for i in 0..<4 {
stringBuilder.append("abc"); //Appends all four Strings from a Dictionary
}
print(stringBuilder); //Output is 0: abc, 1:xyz like that, how to get desired format of that array like ["abc", "xyz"];
Real usage:
let arr = Array(stringReturn.values);
//print(arr) // Great, it prints ["abc","xyz"];
let context = JSContext()
context?.evaluateScript(stringBuilder)
let testFunction = context?.objectForKeyedSubscript("KK")
let result = testFunction?.call(withArguments:arr); // Here when I debugger enabled array is passed to call() like 0:"abc" 1:"xyz". where as it should be passed as above print.
Secondly how to replace escape chars in swift: I used "\" in replaceOccurances(of:"\\'" with:"'"); but its unchanged. why and how to escape that sequnce.
case 2:
string input: \'abc\'
output: 'abc'
To get all values of your dictionary as an array you can use the values property of the dictionary:
let dictionary: Dictionary<String, Any> = [
"key_a": "value_a",
"key_b": "value_b",
"key_c": "value_c",
"key_d": "value_d",
"key_e": 3
]
let values = Array(dictionary.values)
// values: ["value_a", "value_b", "value_c", "value_d", 3]
With filter you can ignore all values of your dictionary that are not of type String:
let stringValues = values.filter({ $0 is String }) as! [String]
// stringValues: ["value_a", "value_b", "value_c", "value_d"]
With map you can transform the values of stringValues and apply your replacingOccurrences function:
let adjustedValues = stringValues.map({ $0.replacingOccurrences(of: "value_", with: "") })
// adjustedValues: ["a", "b", "c", "d"]
Why not try something like this? For part 1 of the question that is:
var stringReturn: Dictionary = Dictionary<String,Any>()
stringReturn = ["0": "abc","1": "def","2": "ghi"]
print(stringReturn)
var stringBuilder = [String]()
for i in stringReturn {
stringBuilder.append(String(describing: i.value))
}
print(stringBuilder)
Also, part 2 seems to be trivial unless I'm not mistaken
var escaped: String = "\'abc\'"
print(escaped)
case 1:
I have Implemented this solutions, Hope this will solve your problem
let dict: [String: String] = ["0": "Abc", "1": "CDF", "2": "GHJ"]
var array: [String] = []
for (k, v) in dict.enumerated() {
print(k)
print(v.value)
array.append(v.value)
}
print(array)
case 2:
var str = "\'abc\'"
print(str.replacingOccurrences(of: "\'", with: ""))
I have an array, of type String:
var Arr = ["apple", "banana", "orange", "grapes", "yellow banana", "urban"]
How do I filter every word in array that has a prefix of my keyword?
Now I have this:
.filter { $0.contains(keyword) }
.sorted { ($0.hasPrefix(keyword) ? 0 : 1) < ($1.hasPrefix(keyword) ? 0 : 1) }
But if I have keyword "ban", it will return "banana", "yellow banana", and "urban".
I need only to filter prefix of every word in array element, to get "banana" and "yellow banana".
You can use a regular expression which checks if the keyword
occurs at a word boundary (\b pattern):
let array = ["apple", "banana", "orange", "grapes", "yellow banana", "urban"]
let keyword = "ban"
let pattern = "\\b" + NSRegularExpression.escapedPattern(for: keyword)
let filtered = array.filter {
$0.range(of: pattern, options: .regularExpression) != nil
}
print(filtered) // ["banana", "yellow banana"]
And for a case-insensitive search use
options: [.regularExpression, .caseInsensitive]
instead.
Performance Note
I just want to refer to the Time-Complexity caused by filtering the array for matching.
For example :
private var words: [String]
func words(matching prefix: String) -> [String]
{
return words.filter { $0.hasPrefix(prefix) }
}
words(matching:) will go through the collection of strings and return
the strings that match the prefix.
If the number of elements in the words array is small, this is a
reasonable strategy. But if you’re dealing with more than a few
thousand words, the time it takes to go through the words array will
be unacceptable. The time complexity of words(matching:) is O(k*n),
where k is the longest string in the collection, and n is the number of
words you need to check.
Trie data structure has excellent performance characteristics for
this type of problem.
Reference : https://www.raywenderlich.com/892-swift-algorithm-club-swift-trie-data-structure
You will need to first break up your string into words using enumerateSubstrings method and then you can check if any of the words contains the keyword prefix:
extension String {
var words: [String] {
var words: [String] = []
enumerateSubstrings(in: startIndex..<endIndex, options: .byWords) { word,_,_,_ in
guard let word = word else { return }
words.append(word)
}
return words
}
}
let arr = ["apple", "banana", "orange", "grapes", "yellow banana", "urban"]
let keyword = "ban"
let filtered = arr.filter { $0.words.contains(where: {$0.hasPrefix(keyword)}) }
filtered // ["banana", "yellow banana"]
Alternatively in Swift 3+:
let array = ["apple", "banana", "orange", "grapes", "yellow banana", "urban"]
let keyword = "ban"
let filtered = array.filter {
$0.components(separatedBy: " ").first { $0.hasPrefix(keyword) } != nil
}
print(filtered) // ["banana", "yellow banana"]
You can filter array with hasPrefix as follows:
// I Use two arrays, actualArray with original data and filteredArray contains data after filtration. You can use whatever is best for your scenario
filteredArray = actaulArray.filter({ $0.hasPrefix(searchBar.text!) })
I'm using xcode 7, and I am wondering how to create a randomized dictionary from two similar arrays.
For example
var array1 = ["apple", "banana", "orange", "strawberry", "cherry"]
var array2 = ["apple", "banana", "orange", "strawberry", "cherry"]
I then want the code to create a random dictionary like so:
var dict = ["apple": "banana", "banana": "apple", "orange": "cherry", "strawberry": "orange", "cherry": "strawberry"]
Also, I don't want to have both value and key to be the same, ie no "apple": "apple".
I'm relatively new to coding. Any help would be greatly appreciated :)
You can use shuffle function from Nate Cook's answer to shuffle values array and then simply fill dictionary with keys and values:
var keys = ["apple", "banana", "orange", "strawberry", "cherry"]
var values = keys
values.shuffle()
var d = [String: String]()
for (index, item) in keys.enumerate() {
d[item] = values[index]
}
The advantage of this solution that it's O(n) (execution time and consumed memory linearly depends from number of items).
Your particular example is a bit contrived as there is really no point in dealing with two identical arrays, you can simply use one. I guess something like this should do the trick:
var fruits = ["apple", "banana", "orange", "strawberry", "cherry"]
var dict = [String: String]()
for (keyIndex, key) in fruits.enumerate() {
var valueIndex: Int {
var index: Int
repeat {
index = Int(arc4random_uniform(UInt32(fruits.count)))
} while index == keyIndex || dict.values.contains(fruits[index])
return index
}
dict[key] = fruits[valueIndex]
}
I have two arrays:
fruitsArray = ["apple", "mango", "blueberry", "orange"]
vegArray = ["tomato", "potato", "mango", "blueberry"]
How can I get the list of common items in those two array which gives
ouptput = ["mango", "blueberry"]
I can't use if contains(array, string) as I want to compare 2 arrays.
You can also use filter and contains in conjunction:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
// only Swift 1
let output = fruitsArray.filter{ contains(vegArray, $0) }
// in Swift 2 and above
let output = fruitsArray.filter{ vegArray.contains($0) }
// or
let output = fruitsArray.filter(vegArray.contains)
Set vs Array for a single computation of common elements
We consider the following code snippet:
let array1: Array = ...
let array2: Array = ...
// `Array`
let commonElements = array1.filter(array2.contains)
// vs `Set`
let commonElements = Array(Set(array1).intersection(Set(array2)))
// or (performance wise equivalent)
let commonElements: Array = Set(array1).filter(Set(array2).contains)
I have made some (artificial) benchmarks with Int and short/long Strings (10 to 100 Characters) (all randomly generated). I always use array1.count == array2.count
I get the following results:
If you have more than critical #(number of) elements converting to a Set is preferable
data | critical #elements
-------------|--------------------
Int | ~50
short String | ~100
long String | ~200
Explanation of the results
Using the Array approach uses "Brute force"-search which has time complexity O(N^2) where N = array1.count = array2.count which is in contrast to the Set approach O(N). However the conversion from Array to Set and back is very expensive for large data which explains the increase of critical #elements for bigger data types.
Conclusion
For small Arrays with about 100 elements the Array approach is fine but for larger ones you should use the Set approach.
If you want to use this "common elements"-operation multiple times it is advisable to use Sets only if possible (the type of the elements has to be Hashable).
Final Remarks
A conversion from Array to Set is kind of expensive while the conversion from Set to Array is in contrast very inexpensive.
Using filter with .filter(array1.contains) is performance wise faster than .filter{ array1.contains($0) } since:
the last one creates a new closure (only once) whereas the first one passes only a function pointer
for the last one the call of the closure creates an additional stack frame which costs space and time (multiple times: O(N))
Convert them to Set and use intersect() function:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
let fruitsSet = Set(fruitsArray)
let vegSet = Set(vegArray)
let output = Array(fruitsSet.intersection(vegSet))
You don't need a Set (as the comments above have mentioned).
You could instead use a generic function, similar to the one Apple use in their Swift Tour, and thus avoid casting:
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
This function can take any two arrays (SequenceTypes) and if any of their elements are the same it returns true.
You could simply modify this generic function to package up an array of strings and return that instead.
For example like this:
func arrayOfCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
returnArray.append(lhsItem)
}
}
}
return returnArray
}
Usage like this:
var one = ["test2", "dog", "cat"]
var other = ["test2", "cat", "dog"]
var result = arrayOfCommonElements(one,other)
print(result) //prints [test2, dog, cat]
The added benefit here is that this function also works with all same typed arrays. So later if you need to compare two [myCustomObject] arrays, once they both conform to equatable, you're all set! (pun intended)
Edit: (For non common elements) you could do something like this
func arrayOfNonCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> [T.Generator.Element] {
var returnArray:[T.Generator.Element] = []
var found = false
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
found = true
break
}
}
if (!found){
returnArray.append(lhsItem)
}
found = false
}
for rhsItem in rhs {
for lhsItem in lhs {
if rhsItem == lhsItem {
found = true
break
}
}
if (!found){
returnArray.append(rhsItem)
}
found = false
}
return returnArray
}
This implementation is ugly though.
A generic method, inspired by The Swift Programming Language (Swift 3) exercise:
func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
var common: [T.Iterator.Element] = []
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
common.append(lhsItem)
}
}
}
return common
}
Then, use it like this:
var a = [3,88,74]
var b = [1,3,88]
print("commons: \(commonElements(a, b))")
--> commons: [3, 88]
The following works with swift 4:
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
var someHash: [String: Bool] = [:]
fruitsArray.forEach { someHash[$0] = true }
var commonItems = [String]()
vegArray.forEach { veg in
if someHash[veg] ?? false {
commonItems.append(veg)
}
}
print(commonItems)
The following works with swift 4,5
let fruitsArray = ["apple", "mango", "blueberry", "orange"]
let vegArray = ["tomato", "potato", "mango", "blueberry"]
//You can using filter function
let commonArray = fruitsArray.filter { fruit in
vegArray.contains(fruit)
}
print(commonArray)
Output: ["mango", "blueberry"]
Using Set and also intersection as follows:
func findIntersection (firstArray : [Int], secondArray : [Int]) -> [Int]
{
return [Int](Set<Int>(firstArray).intersection(secondArray))
}
print (findIntersection(firstArray: [2,3,4,5], secondArray: [1,2,3]))
I have dictionary like this
var dict : [String : Array<String>] = ["Fruits" : ["Mango", "Apple", "Banana"],"Flowers" : ["Rose", "Lotus","Jasmine"],"Vegetables" : ["Tomato", "Potato","Chilli"]]
I want to get values in array for each key How to get it in swift?
2½ years and no-one mentioned map?
ok, set aside that Dictionary has a property values(as ameenihad shows) which will do what you asking for, you could do:
let values = dict.map { $0.value }
try this:
for (key, value) in dict {
println("key=\(key), value=\(value)")
}
Try to get values as like following code
let fruits = dict["Fruits"]
let flowers = dict["Flowers"]
let vegetables = dict["Vegetables"]
Try:
var a:Array = dict["Fruits"]! ;
println(a[0])//mango
println(a[1])//apple
for val in dict.values {
print("Value -> \(val)")
}
EDIT:
Try Something like this,
var dict : [String : Array<String>] = [
"Fruits" : ["Mango", "Apple", "Banana"],
"Flowers" : ["Rose", "Lotus","Jasmine"],
"Vegetables" : ["Tomato", "Potato","Chilli"]
]
var myArray : Array<String> = []
// You can access the dictionary(dict) by the keys(Flowers, Flowers, Vegetables)
// Here I'm appending the array(myArray), by the accessed values.
myArray += dict["Fruits"]!
myArray += dict["Vegetables"]!
print("myArray \(myArray)")
Above is how to get values of dictionay in swift,
If you want to get contatenated array of all the values of
dictionary(*only values),
Try something like below.
print("values array : \(dict.map{$0.value}.flatMap{$0})")
values array : ["Rose", "Lotus", "Jasmine", "Tomato", "Potato",
"Chilli", "Mango", "Apple", "Banana"]