4

I am a little stumped by this but I want to achieve the following.

I have a large string and within that string I want to match against an array of strings and replace with markup tags.

Take the following string:

The quick brown fox jumps over a lazy dog.

This is my list of strings ( which could be a whole sentence not just a word) I wish to match to the body of text:

['quick', 'brown', 'lazy dog', '.']

My result I am trying to achieve:

// ['The', <span>quick</span>, '<span>brown</span>', 'fox jumps over a' '<span>lazy dog</span>', '<span>.</span>]

Caveats and additional notes:

  • Avoid using DOM manipulation like addClass, innerHTML, appendChild, as I would like to come up with an elegant solution within the render function of a component with in React
  • Not to use a complete string regex solution as I am wanting to insert react DOM elements instead of manipulating and parsing the dom after the solution
  • I want to attach additional information to the span tags IE the react DOM elements to filter by these wrapped strings, for example ID's or native react events to that element
  • I must cater for matching sentences not just individual words.
  • No browser requirements
  • List of strings will always be unique and have no overlaps
  • Not all strings in the list are within the body of text ( ;) )

Kinda the dataset I would be dealing with: https://codepen.io/nigel_manny/pen/omjxrx

Goodluck and thank you!

8
  • What will be the output if the list of strings is ['quick', 'quick brown', 'lazy dog', '.']? Commented Jan 24, 2019 at 15:21
  • an array item will always be unique and never contain another word from another sentence Commented Jan 24, 2019 at 15:24
  • okay. so I assume there won't be overlap of words. Commented Jan 24, 2019 at 15:25
  • That is correct! Nigel Commented Jan 24, 2019 at 15:26
  • Never seen so many self-deleted answers in a while, good question ! Commented Jan 24, 2019 at 15:36

4 Answers 4

3

I think the trick here is to divide and conquer the string, finding and wrapping the word/sentence matched on the way with HTML element and repeat the process for each word/sentence.

Walkthrough:

// Sample data
const str = "The quick brown fox jumps over a lazy dog.";
const words = ["quick", "brown", "lazy dog", ".", "missing word"];

Let's put str in a array and in the beginning str is the only element in the array.

// Start
["The quick brown fox jumps over a lazy dog."]

// Iteration 1: replace all "quick" with span
// When matched, we split current element into three array elements
// Before match part + matched part + after match part
["The ", <span>quick</span>, " brown fox jumps over a lazy dog."]

// Iteration 2: replace all "brown" with span
["The ", <span>quick</span>, " ", <span>brown</span>, " fox jumps over a lazy dog."]

// Iteration 3: replace all "lazy dog" with span
["The ", <span>quick</span>, " ", <span>brown</span>, " fox jumps over a ", <span>lazy dog</span>, "."]

// Iteration 4: replace all "." with span
["The ", <span>quick</span>, " ", <span>brown</span>, " fox jumps over a ", <span>lazy dog</span>, "", <span>.</span>, ""]

// Iteration 5: replace all "missing word" with span, but this sentence doesn't exist, so output will remain same
["The ", <span>quick</span>, " ", <span>brown</span>, " fox jumps over a ", <span>lazy dog</span>, "", <span>.</span>, ""]

Here is the working solution 👇

Edit 8nkp163270

Sign up to request clarification or add additional context in comments.

5 Comments

Hey I like the look of this let me check it out :peace:
Updated with bit modified real data.
Thats great. I will take a look it in more detail but going to upvote for now
Please mark as accepted answer if it works out. I liked your problem. it was interesting to solve.
Sure when I get back on my laptop but this is exactly the solution I was hoping to see more of. I will mark as accept later most likely dude! good work
1

My proposal, with updated data, is (no more regex):

const text = 'Could it be improved where an ai suggests text books and notes which could help with a question (maybe based on keywords?) and then at the end the user rates the helpfulness of the suggestions, which helps the ai learn what to suggest. Taking lead from Medium articles, it would be awesome to be able to highlight text and it gives you suggestions of what to do with it (copy to clipboard, bookmark to my account etc) and also show me the most highlighted sections of each set of notes so i see what the community is finding most useful. I think linking to buy the paper version of the book is a weak and half hearted way to monitise this idea - why not go "full netflix" and get rid of the blockbusters model altogether. Scrap all the print titles and charge a subscription to be able to be able to access the extra revision information. In a Spotify freemium kind of way you could access the question banks for free but to get the revision notes/books etc you would pay. You would need a subscription model which worked for the amount of time someone is likely to find this information useful. Maybe micropayments solution would be better than a subscription?';

const matchingSentences = [
    'Could it be improved where an ai suggests text books and notes which could help with a question (maybe based on keywords?) and then at the end the user rates the helpfulness of the suggestions, which helps the ai learn what to suggest.',
    'Taking lead from Medium articles, it would be awesome to be able to highlight text and it gives you suggestions of what to do with it (copy to clipboard, bookmark to my account etc) and also show me the most highlighted sections of each set of notes so i see what the community is finding most useful.',
    'I think linking to buy the paper version of the book is a weak and half hearted way to monitise this idea - why not go "full netflix" and get rid of the blockbusters model altogether.',
    'Scrap all the print titles and charge a subscription to be able to be able to access the extra revision information.'
];

var result = [];
var startingIdx = 0;

matchingSentences.forEach(function(e, i) {
    var idx = text.indexOf(e);
    if (idx != -1) {
        if (idx != startingIdx) {
            result.push(text.substr(startingIdx, e.length));
        }
        var str = '<span>' + e + '</span>';
        result.push(str);
        startingIdx += (e.length + 1);
    }
});
if (startingIdx < text.length) {
    result.push(text.substr(startingIdx));
}

console.log(result);

5 Comments

This is ok and i get it but i have stated : "Avoid using DOM manipulation like addClass, innerHTML, appendChild, as I would like to come up with an elegant solution within the render function of a component with in React" ;)
I see how this works but this only returns it as a string as I do not wish to parse a string into HTML
@Nige ...so, what are you looking for? An array per result?
The outputted result should be an array as specified in the question. The replacing or finding shouldnt be done with any string regex/replace functions as I do not wish to parse this to HTML.
@Nige OK, may you test the last snippet now?
0

The imperative and naive way would be to cycle through each word in the array and check the appearances in the sentence:

const string = "The quick brown fox jumps over a lazy dog. A full sentence.";
const replaceWords = ['quick', 'brown', 'lazy', '.', 'A full sentence.'];
const stringAsArray = string.replace('.', ' .').split(' ');

const splittedReplaceWords = replaceWords.flatMap(word => word.replace('.', ' .').split(' '));

const jsxEnhancedText = splittedReplaceWords.map(word => {
    if(stringAsArray.indexOf(word) > -1) {
        return React.createElement('span', props, word);
    }

    return word;
})

React.createElement is just the non JSX variant. React.createElement('span', props, word) takes an element as the first parameter (e.g. 'span' or a custom one), props as an array as second parameter and then the children (in this case the word). See https://reactjs.org/docs/react-api.html#createelement for more information on this.

2 Comments

I updated the question the list of strings might be sentences not just indivudual words.
Updated the answer.
0

you can split the string and then match every word with list elements and store them in a separate list.

your code may look like this.

const stringText = 'The quick brown fox jumps over a lazy dog.';
const matchList = ['quick', 'brown', 'lazy', '.'];
const splittedText = stringText.replace('.', ' .').split(' ');
const matchedSplittedText = splittedText.map(word => matchList.includes(word) ? handleWord(word) : word);

2 Comments

won't help with punctuations
I like the solution but I made a mistake. I updated the question- the list of strings might be sentences not just singular words.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.