Search Multiple Words (or Characters) in String Array (swift) - arrays

I am developing an application for words.
My problem resembles the problem: this
But, I want to do the search in the string array.
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
// myArr.contains(findChrs) //so error
I want to: return 3 or "BERLIN","ISTANBUL" and "WIEN" (Contains "I" and "N")
I tried it, but it's too long...(I look individually. Too many words.):
for i in 0...myArr.count - 1 {
let success = !findChrs.contains(where: { !myArr[i].contains($0) })
if success {
print(myArr[i])
}
}
Is there an easier way? Thank you so much.

There's a swifty way to solve your problem
I started with using just one filter on the myArr array using hardcoded search terms
let filteredStrings : [String] = myArr.filter({
return $0.contains("I") && $0.contains("N")
})
but thats going to help only if your findChars are always going to be I and N only.
Later I realized how to do it without hardcoding the find char characters:
ANSWER 1
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
let filteredStrings : [String] = myArr.filter({ (aString) in
let hasChars = findChrs.filter({(bString) in
return aString.contains(bString)
})
print(hasChars)
return hasChars.count == findChrs.count
})
print(filteredStrings)
Instead of using $0, I think in the second chunk of code, its easier to understand whats happening with aString and bString.
Do check the code in a playground and see how this code works. If you haven't used the higher order functions, it can be a little daunting to understand filters without a playground and the print statements.
Update:
Was just thinking about this problem and I gave this alternate approach a try, using sets, map and filter. It is super swifty, and can be difficult to read/understand:
ANSWER 2, concise
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = ["I","N"]
let finderSet:Set<String> = Set(findChrs)
let filteredArray = myArr.filter {
return Set($0.characters.map({String($0)})).intersection(finderSet).count == findChrs.count
}
For the sake of readability and ease of understanding, here's what is happening:
Answer 2, verbose
let filteredArray = myArr.filter { (aString) -> Bool in
//for each string in myArr, convert it into an array of string chars
let stringArray = aString.characters.map({aCharacter in
String(aCharacter)
})
//convert string array into a set
let aSet = Set(stringArray)
// find the intersection (common elemnts from aSet and finderSet)
let intersect = aSet.intersection(finderSet)
//return true if aString has all of findChrs elements
return intersect.count == findChrs.count
}
Both Answer 2 'concise' and 'verbose' will give you the same results.
Based on some simple code execution time check, it looks like Answer 1 is ~3x faster than Answer 2. So, Answer 1 is still a better choice and probably easier to understand.
Hope this helps anyone reading the answer understand filter and map!

You could also use a set for the filter provided you're not looking for patterns that contain repeated letters:
let myArr: [String] = ["BERLIN","ISTANBUL","TOKYO","NEWYORK","PRAGUE","WIEN"]
let findChrs = Set<Character>(["I","N"]) // let findChrs = Set("IN")
let matchingCities = myArr.filter{ findChrs.isSubset(of:$0) }

Related

Using returned value (array of Strings) in another Swift file

Apologize for a complete newbie question. This was the original array of Strings I wrote:
let fruit = ["apple1.jpg", "apple2.jpg", "apple3.jpg", ..... "apple10.jpg"]
First, I made a function in a separate Swift file (attached to the project) to replace above array, as the array's content might be changed based on several input factors later:
class Fruits {
let fruit = "apple"
func fruitName() -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
}
}
This is everything written on Fruits.swift file. And then, back to original file, I wanted to replace the original let fruit = ["", "", ...] to something like let fruit = Fruits.fruitName() - by loading the returned arrayA. But it was a bit confusing to understand how to use returned String Array values in a different file, inside of a different Class bracket. I tried something like let fruits = Fruits(), let fruit = fruits.fruitName(), etc but it doesn't seem to successfully replace the original array code. I still need to create the constant let fruit = part. Is there any way to load the returned value in a separate file? Much appreciated. <3
If you want a property of a class to be directly accessible from anywhere in your code, you can make it static:
class Fruits {
static let fruit = "apple"
static func fruitName() -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
}
// usage:
let fruits = Fruits.fruitName()
Depending on your specific situation, you could even not have a class and have a global function that takes the fruit as a parameter:
func fruitNames(fruit: String) -> [String] {
let arrayA = (1...10).map({ "\(fruit)\($0).jpg" })
return arrayA
}
// usage:
let fruits = fruitNames(fruit: "apple")

"hasPrefix" not working in Swift

I have an array of dictionary & I try to filter with prefix from using one of the key as follow:
let b = [["id":1,"name":"India"],["id":2,"name":"america"],["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],["id":6,"rec_name":"Zimba"]]
let g = b.filter({String(describing: $0["name"]).hasPrefix("I")})
print(g) //retun Empty array
If I try with contains then it working fine
or
If I try with only pure String array then also it working fine
Thank you,
Since your array (b) contains dictionaries where name is not always present (you have an object with rec_name), you could modify your filter to something like this:
let g = b.filter {
guard let name = $0["name"] as? String else { return false }
return name.hasPrefix("I")
}
and thus making sure that only dictionaries with a value for that key are matched by your filter
Fun fact: The reason why the original code doesn't work (as Martin points out) is pretty obvious when we do something like this:
let g = b.map({String(describing: $0["name"])})
print(g)
Which prints:
["Optional(\"India\")", "Optional(\"america\")", "Optional(\"africa\")", "Optional(\"indonesia\")", "Optional(\"jakarta\")", "nil"]
Please check with this.
let b = [["id":1,"name":"India"],["id":2,"name":"america"],["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],["id":6,"rec_name":"Zimba"]]
let g = b.filter({String(describing: $0["name"] as? String ?? "").hasPrefix("I")})
print(g)
Try this code you'll get the proper solution.
let b = [["id":1,"name":"India"],["id":2,"name":"america"],
["id":3,"name":"africa"],["id":4,"name":"indonesia"],["id":5,"name":"jakarta"],
["id":6,"rec_name":"Zimba"]]
let g = b.filter({($0["name"] as? String ?? "").hasPrefix("I")})
print(g)

How to get an array with Name stored as a string in swift

I have a program in which I have to make an string array equal to another array. The second array needs to be found by its name,
So for example something like this :
let StoreString1 = ["", ""]
let StoreString2 = ["", ""]
let FinalString = GetStringWithName("StoreString" + Number)
in C# its GetComponent("ComponentName");
Thanks for all answers, and sorry for the confusing way I wrote the question, because I didn’t really know hot to put it into words XD.
What you are trying to do can be achieved by using Dictionaries in swift. It appears that you are not familiar with this topic, so will be of no use to just throw out some code, there are many tutorials about this regard.
Just for mentioning one, (that has even images explaining how it works) you can enter here
Happy Learning & Coding! ;-)
UPDATE:
Here's a playground testing the concept: (Feel free to adjust it to your needs)
var dictionary: [String : [ String ]] = ["" : []]
let storeString = "StoreString"
func addUpdateArray(strings: [String], index: Int) {
let locator = storeString + index.description
dictionary[locator] = strings
}
func getStringWitNameIndex(index:Int) -> [String]? {
return dictionary[ storeString + index.description]
}
func addToArray(index:Int, inout destiny: [String]) {
if let array = getStringWitNameIndex(index) {
destiny = destiny + array
}
}
addUpdateArray(["Hello", "World"], 1)
addUpdateArray(["Hello", "Cats"], 3)
var finalArray : [String] = []
addToArray(1,&finalArray)
addToArray(3,&finalArray)
finalArray
In this case, finalArray ends up having: ["Hello", "World", "Hello",
"Cats"]

Convert [String]? to String in Swift

For my project, I extracted tweets from a CSV file in Swift. Problem is now all tweets are parsed as one element in an array, separated by ",".
let tweetsOfColumns = columns["tweet"]
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
Error message: '[String]?' does not have a member named
'componentsSeparatedByString'.
I checked if tweetsOfColumns contains multiple elements, but it doesn't allow me to subscript with tweetsOfColumns[index].
Looking at the link you reference, columns["tweets"] is going to give you back an array of the values from the "tweets" column, so it's what you need already, there's no additional comma's to split things on, you just need:
let seperatedColumns = columns["tweet"]
to have an array containing the tweet column for each row.
When you try to get an element from a dictionary, like
columns["tweet"]
it will give you back an optional, because if there is nothing associated with the key, it gives you back nil (None), otherwise the value wrapped in an optional (Some(data)).
So you have to unwrap the optional for example:
columns["tweet"]!
You have to either use the optional ? to access the string:
let seperatedColumns = tweetsOfColumns?.componentsSeparatedByString(",")
But you should unwrap it:
if let unwrappedTweets = tweetsOfColumns?.componentsSeparatedByString(","){
let seperatedColumns = unwrappedTweets
}
The problem is probably that you'll get an optional back, which you have to unwrap. And the easiest and most elegant is to use the if-let unwrapper.
if let tweetsOfColumns = columns["tweet"] {
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
// do something with the seperatedColumns
}
Based on David's question and the OP's response in the OP comments, you can use map on the Array returned by columns["tweet"]. Please post actual data/code in the future.
let columns = [
"tweet":["handleX,tag1,tag2,textA,textB",
"handleY,tag1,tag2,textC,textD"]]
var chunk = [[String]]()
if columns["tweet"] != nil {
chunk = columns["tweet"]!.map {
return $0.componentsSeparatedByString(",")
}
}

Convert Parse.com json array into Array with swift

Can someone please help me with this. I saved my data into Parse.com into column with type array (example: ["11:30","12:45,"13:02"], just some list of some times as string). I have tried to get this data with swift:
var take: NSMutableArray!
var query = PFQuery(className: "test")
query.getObjectInBackgroundWithId("QZ6Y8Oljc5"){
(testData: PFObject!, error: NSError!) -> Void in
if (error == nil){
take = testData["workday"]
println(take)
}
else{
println(error)
}
}
the problem is that i get only json array type:
(
"11:30",
"12:45,
"13:02"
)
How can I convert it into NSArray so it could be like:
var myArray = ["11:30","12:45,"13:02"]
Thank you for any suggestions because I tried every method I found here, but without any results.
The problem with JSON data is that it is it's own array that has to be sifted and groomed. normally people would end up using huge nested IF statements which ends up looking messy. Luckily, someone created a code that sifts through JSON data and gives you back usable types (Int, Arrays, Strings) by use of a massive switch table.
https://github.com/SwiftyJSON/SwiftyJSON
Look it up, it should help. Once you have it implemented you can call it by typing..
let json = JSON(Data : JSONData)
then to sift through, you use substrings.. (depending on the data, you match it with a string or int)
let firstIndex = json["workday"]
//Int
let firstIndexOfWorkDay = json["workday"][0]
//String
let firstIndexOfWorkDay = json["workday"]["time"]
and so on... however, you will need to cast it once you singled out the data
let firstIndexOfWorkDay = json["workday"][0].valueOfFloat
//printing it would give 11:30
although personally I use ".description" .. since sometimes when I sift through all the array, its a mix of types.
let firstIndexOfWorkDay = json["workday"][0].description
println(firstIndexOfWorkDay)
//would literally give "11:30" including the quotation marks
then I use string methods to trim the quotations then cast it to whatever type I need. But it's up to your creativity once you figure out how it works

Resources