The setup: A UITextField and a Tableview with suggested users
I try to have the following result:
I want users to be able to link other users.
Its working fine as long as I search with my last word in the array
let caption = captionTextView.text
let words = caption?.components(separatedBy: .whitespacesAndNewlines)
guard let searchingWord = words?.last else {return}
if searchingWord.hasPrefix("#") {
self.indicator.startAnimating()
let search = searchingWord.trimmingCharacters(in: CharacterSet.punctuationCharacters).lowercased()
}
But in case a user wants to adjust a username in the middle or at least not at the end of the array, the searching functions doesn't work properly as it still searches with the last word in the array
Example:
"Hey how are you #Lisa #Marcel #Thomas"
In case a user wants to change "#Lisa" to "#Lisbeth" the search function will search with Thomas as its the last word in the array
I wasn't able to get the word I am working at, only last and first words in the array, however I am able to get the current cursor location with
let cursor = captionTextView.cursorOffset!
which is an extension.
So how do I get the word I am working at up until the next "#" to the left und the next blank space to the right? Thanks in advance!
Maybe try something like this:
if let selectedRange = textview.selectedTextRange {
let cursorOffset = textview.offset(from: textview.beginningOfDocument, to: selectedRange.start)
let text = textview.text
let substring = text?.prefix(cursorOffset)
let editedWord = substring?.split(separator: "#")
}
(written on a phone, and untested)
One solution is Regular Expression
let string = "Hey how are you #Lisa #Marcel #Thomas"
let searchingWord = "Lisa"
let replacingWord = "Lisbeth"
let pattern = "#\(searchingWord)\\s"
string.replacingOccurrences(of: pattern, with: "#\(replacingWord) ", options: .regularExpression)
The pattern searches for # followed by the searching word followed by a whitespace character.
Since you say things are working the way you want if the last word is the one that has a username in it you just need to loop over all the words. Depending on your needs you may need to keep track of the usernames that were in the text before to save you from searching for the same user multiple times, but an array of used usernames should sort that for you.
Also, unless you want to prevent users from having underscores and the such in their names you should tweak the way in which you remove the # symbol as well.
guard let words = captionTextView.text?.components(separatedBy: .whitespacesAndNewlines) else { return }
for word in words where word.hasPrefix("#") {
self.indicator.startAnimating()
let search = word.replacingOccurrences(of: "#", with: "").lowercased()
}
Sticking the above code into a playground that uses the sample string you supplied in place of captionTextView.text? and printing search each time yielded…
lisa
marcel
thomas
Related
I'm working on a command that searches maps for the game osu! on the site bloodcat.com and I made a way to filter the maps with:
.split('status=ranked').join('&c=b&s=1&m=&g=&l=')
.split('status=approved').join('&c=b&s=2&m=&g=&l=')
.split('status=qualified').join('&c=b&s=3&m=&g=&l=')
.split('status=loved').join('&c=b&s=4&m=&g=&l=')
.split('status=unranked').join('&c=b&s=0&m=&g=&l=')
.split('status=graveyarded').join('&c=b&s=0&m=&g=&l=');
Now someone can do !search 'map name' status=unranked and that would look for https://bloodcat.com/osu/?mod=json&q='map name'&c=b&s=0&m=&g=&l= but if someone does for example !search status=unranked 'map name' that doesn't work is there any way.
Can I make the status=unranked at the end even if the user doesn't put it in the end in the command?
I would do it like this: first, check if the string of arguments starts with status=. If so, you'll need to switch them, otherwise, it should work as it's doing now.
In order to switch those, I would split the string with spaces, remove the first argument (status=*), rejoin that with spaces and add the removed part:
function getQueryURL(argStr = "") { // argStr should be like "'map name' status=unranked" OR "status=unranked 'map name'"
if (argStr.startsWith('status=')) {
let arr = argStr.split(' ');
let removed = arr.shift();
argStr = arr.join(' ').trim() + removed.trim();
}
let res = argStr.split('status=ranked').join('&c=b&s=1&m=&g=&l=')
.split('status=approved').join('&c=b&s=2&m=&g=&l=')
.split('status=qualified').join('&c=b&s=3&m=&g=&l=')
.split('status=loved').join('&c=b&s=4&m=&g=&l=')
.split('status=unranked').join('&c=b&s=0&m=&g=&l=')
.split('status=graveyarded').join('&c=b&s=0&m=&g=&l=');
return "https://bloodcat.com/osu/?mod=json&q=" + res;
}
Let's say I have a variable of type String holding a single String. In this string, there are 1 or more words. I also have an array of Strings. I want to check the String against the array, looking for, and replacing words found in the array.
For instance, the word "able" is in the array. The word "able" is also in the String variable. I want the word able replaced with the "+" character.
I've tried like this:
//stopWords = array of Strings / keywords = String variable
for words in stopWords {
keywordsFormatted = keywords.replacingOccurrences(of: words, with: "+", options: .regularExpression, range: nil)
}
and that doesn't change anything. I've also tried in a while loop, and a few other ways that I don't even care to recall right now lol. Any help is appreciated
Given
let stopWords = ["red", "green", "blue"]
let keywords = "The sky is blue not green"
let's create a set
let stopWordsSet = Set(stopWords)
and finally let's solve the problem
let result = keywords
.components(separatedBy: " ")
.map { stopWordsSet.contains($0) ? "+" : $0 }
.joined(separator: " ")
Oh... and lets test it
print(result) // The sky is + not +
Please note this solution is case sensitive. Furthermore it will not work is words in keywords are not delimited by spaces. E.g. "The sky is blue, not green" will not work properly because of the ,.
Addendum from Adrian:
If you want to update the textField, just use didSet, like so:
var keywords = "The sky is blue not green" {
didSet {
// if keywords is not empty, set the myTextField.text to keywords
if !keywords.characters.isEmpty {
myTextField.text = keywords
}
}
}
this is my first post and question. Here is what i'm doing to collect hashtags from UIText Field. It's working but I think this is not the correct way to do this. Any ideas? I need an array of hashtags as words.
Need to collect these type of tags: "Hey, this is my first photo!#photo #hashtag #daily:)
Here is my code;
// Prepare words
let words:[String] = titleTxt.text!.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
self.taggyArray.removeAll(keepCapacity: false)
// define tagged word if #
for var word in words {
if word.hasPrefix("#") {
// cut symbold
word = word.stringByTrimmingCharactersInSet(NSCharacterSet.punctuationCharacterSet())
word = word.stringByTrimmingCharactersInSet(NSCharacterSet.symbolCharacterSet())
// now prepare adjacent tags
let tags:[String] = word.componentsSeparatedByCharactersInSet(NSCharacterSet.init(charactersInString: "#"))
for tag in tags {
taggyArray += [tag]
}
}
}
And save to server
object["hashtags"] = taggyArray
Is it possible to work this just with one regex seperation? Thank you,
Try this.
let matches = regex.matchesInString("(\#\w+)", ""Hey, this is my first photo!#photo #hashtag #daily:)")
println(matches)
I'm really fresh to Python and need help reading information from txt file. I have a large C++ app need to duplicate it in Python. Sadly I have no idea where to start. I've been reading and watching some tutorials, but little help from them and I'm running out of time.
So my task is:
I have a shopping list with:
-Name of the item, price and age.
I also need to create two searches.
Search whether the item is in the shop (comparing strings).
if name of the item is == to the input name.
Search by age. Once the program finds the items, then it needs to print the list according to the price - from the lowest price to the highest.
For example you input age 15 - 30, the program prints out appropriate
items and sorts them by the price.
Any help would be nice. At least from where I could start.
Thank you.
EDITED
So far, I have this code:
class data:
price = 0
agefrom = 0
ageto = 0
name = ''
# File reading
def reading():
with open('toys.txt') as fd:
toyslist = []
lines = fd.readlines()
for line in lines:
information = line.split()
print(information)
"""information2 = {
'price': int(information[1])
'ageftom': int(information[2])
'ageto': int(information[3])
#'name': information[4]
}"""
information2 = data()
information2.price = int(information[0])
information2.agefrom = int(information[1])
information2.ageto = int(information[2])
information2.name = information[3]
toyslist.append(information2)
return toyslist
information = reading()
I HAVE A PROBLEM WITH THIS PART. I want to compare the user's input with the item information in the txt file.
n_search = raw_input(" Please enter the toy you're looking for: ")
def name_search(information):
for data in information:
if data.name == n_search:
print ("We have this toy.")
else:
print ("Sorry, but we don't have this toy.")
If you want to fins something in a list it's generally as straightforward as:
if "apple" in ["tuna", "pencil", "apple"]
However, in your case, the list to search is a list of lists so you need to "project" it somehow. List comprehension is often the easiest to reason about, a sort of for loop in a for loop.
if "apple" in [name for name,price,age in [["tuna",230.0,3],["apple",0.50,1],["pencil",1.50,2]]]
From here you want to start looking at filters whereby you provide a function that determines whether an entry is matched or not. you can roll your own in a for loop or use something more functional like 'itertools'.
Sorting on a list is also easy, just use 'sorted(my_list)' supplying a comparator function if you need it.
Examples as per your comment...
class ShoppingListItem:
def __init__(self,name,price,age):
self.name=name
self.price=price
self.age=age
or
from collections import namedtuple
sli = namedtuple("ShoppingListItem",['name','age','price'])
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(",")
}
}