DEV Community

HarmonyOS
HarmonyOS

Posted on

Highlighting Keywords and Consecutive Numbers in Search Results

Read the original article:Highlighting Keywords and Consecutive Numbers in Search Results

Context

Issue with highlighting keywords in the prompt word array when displaying search results. Regular matching works for words, but does not properly highlight consecutive numbers (e.g., "1998").

Description

When highlighting search results based on an array of prompt words (e.g., ["you", "good", "1998"]), the following cases should be handled:

  1. Result: "Hello, please answer 1998" → words "you", "good", and "1998" highlighted in red.
  2. Result: "You are a good person" → words "you" and "good" highlighted in red.
  3. Result: "I am your good man" → words "you", "good", and repeated "good" highlighted in red.
  4. Result: "1998456" → "1998" highlighted in red, while "456" remains black.

The challenge: normal regex-based splitting doesn’t correctly separate numbers like 1998 when they appear inside larger digit sequences.

Solution / Approach

To solve this, a string splitting function with regex handling is implemented. It ensures:

  • Chinese characters are matched individually.
  • Numbers are matched as consecutive groups.
  • Special handling is applied to correctly extract and highlight 1998 even when embedded in longer numbers.

Here you can find the related example below;

@Entry
@Component
struct HighlightDemo {
  @State bookName: string = 'Hello, please answer 112319981, you are a good person, I am your good man, 1998456';
  @State tokenWord: string[] = ["","","1998"]

  /**
   * Split the search result string into an array
   */
  splitString(input: string): string[] {
    // Match Chinese characters or consecutive numbers
    const regex = /[\u4e00-\u9fa5]|\d+/g;
    const matches = input.match(regex);
    let regNumber = new RegExp('[0-9]+')

    matches?.forEach((value: string, index: number) => {
      if (regNumber.test(value)) {
        if (value.indexOf('1998') == 0) {
          matches.splice(index, 1, '1998')
          matches.splice(index + 1, 1, value.substring(4, value.length))
        } else {
          matches.splice(index, 1, value.substring(0, value.indexOf('1998')))
          matches.splice(index + 1, 1, '1998')
          matches.splice(index + 2, 1, value.substring(value.indexOf('1998') + 4, value.length))
        }
      }
    })
    console.log('matches', matches)
    return matches || []
  }

  build() {
    Column() {
      Text() {
        ForEach(this.splitString(this.bookName), (itemStr: string) => {
          Span(itemStr)
            .attributeModifier(new HighlightModifier(itemStr, this.tokenWord, 13))
            .fontWeight(FontWeight.Medium)
        })
      }
    }
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

/**
 * Keyword highlighting in search results
 */
export class HighlightModifier implements AttributeModifier<SpanAttribute> {
  textItem: string = ''
  cueWords: string[] = []
  fontSize: number = 12

  constructor(textItem: string, cueWords: string[], fontSize: number) {
    this.textItem = textItem
    this.cueWords = cueWords
    this.fontSize = fontSize
  }

  applyNormalAttribute(instance: SpanAttribute): void {
    instance.fontColor(
      this.cueWords.includes(this.textItem)
        ? $r('app.color.migu_color_gb_red')
        : $r('app.color.migu_color_text_black')
    ).fontSize(this.fontSize)
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Use regex (/[\u4e00-\u9fa5]|\d+/g) to split text into meaningful tokens: Chinese characters or numeric groups.
  • Apply special case handling for substrings like "1998" within larger numbers.
  • Use a custom AttributeModifier to highlight only matching tokens with a specific color.

Written by Mehmet Algul

Top comments (0)