Home > OS >  How to find and underline all once and twice used keywords in a NSString array
How to find and underline all once and twice used keywords in a NSString array

Time:03-05

I have an array of strings. I need a way to find all keywords (once or twice used) and underline them. At the moment, I'm converting them to NSMutableAttributedString which is working, but that just displays the text. Is there a way to write a for loop or something that would find all the ranges of keywords and apply underline attribute to them? Am I doing it all wrong? I'm teaching myself Swift by writing an app instead of taking a course... probably not very smart. Thank you for your help!

I understand that having the client prosses the strings for each keyword each time it is run is very ineffieciet. The strings need to be easily updated, so (for now) it's faster to prosses each time.

Code (Underline occurrences of keyword swift iOS NSMutableAttributedString NSAttributedString

Update

Based on Jon's (OP) comments:

How do I split it back into the original array? I need the original array with the added underlines.

In order to that, we need to keep track of the word count as before using the hash table. However, the range will not be enough as we needed the added information of which string in the original array the word belongs to.

In order to do that, I created a struct MatchProperty to store this information:

struct MatchProperty
{
    // Stores the index of which array the word
    // was encountered
    var index: Int
    
    // Stores the range of the word in the string
    var range: NSRange
}

Then I made some small updates to the logic

// Array of strings
let strings = ["This will occur twice",
               "Will also occurs two times",
               "But where is something else",
               "Other things happen once this time"]

// Hash to store words and where they occurred
var wordOccurrences: [String: [MatchProperty]] = [:]

// Loop through the string in the strings array
for (index, string) in strings.enumerated()
{
    // Loop through all the words in each string of the string array
    string.enumerateSubstrings(in: string.startIndex...,
                               options: .byWords)
    { word, substringRange, _, _ in
        
        // Lower case the word
        if let word = word?.lowercased()
        {
            // Check if we have come across the word before or not
            // If we have, retrieve the match property for the word
            // If not, we will initialize a new array to store the match
            // properties of the first occurrence
            var matchesInfo = wordOccurrences[word, default: []]
            
            // Create a match location object with the index of where
            // the word occurs in the strings array along with its range
            let matchProperty = MatchProperty(index: index,
                                              range: NSRange(substringRange,
                                                             in: string))
            
            // Store the match
            matchesInfo.append(matchProperty)
            wordOccurrences[word] = matchesInfo
        }
    }
}

// This will be used to store the updated versions of the strings
// with the underlines
var updatedStrings
    = strings.map { return NSMutableAttributedString(string: $0) }

// Loop through the occurrences of words
for occurrence in wordOccurrences.values
{
    // Check which words occurred twice
    if occurrence.count == 2
    {
        // Iterate over the occurrences
        for matchProperty in occurrence
        {
            // Retrieve the attributed version of the string
            let attributedString = updatedStrings[matchProperty.index]
            
            // Underline words that have occurred twice
            attributedString.addAttribute(.underlineStyle,
                                          value: NSUnderlineStyle.double.rawValue,
                                          range: matchProperty.range)
            
            // Update the attributed string data
            updatedStrings[matchProperty.index] = attributedString
        }
    }
}

// I am joining all the updated versions of the original string
// This is not needed, I am just using it to display the original
// strings but with their updates
// REF: https://stackoverflow.com/a/48583402/1619193
let attributedString = updatedStrings.joined(separator: "\n")

// Set the attributed text where you want
label.attributedText = attributedString

The function joined is not something that comes by default for arrays of NSMutableAttributedStrings, so I used NSAttributedString underline occurrences of string swift iOS

  • Related