Find and replace by a dictionary, using keys only once


<em>This is a follow-up of <a href="https://stackoverflow.com/questions/8413651/find-multiple-key-words-within-a-dictionary-javascript" rel="nofollow">Find multiple keywords within a dictionary</a>.</em>

My questions are...


The first is: I believe this matches words that are not whole. Like if short is in my dictionary it matches the word shortly. How would I stop this?

</li> <li>

And the second not so important but would be nice is: How would I make it so it only matches once per content? So short doesn't get defined twice within the same content area.

</li> </ol>



I have implemented the following additional requirements:

<ul><li>Do not match shortly when looking for short (because shortly is a different word)</li> <li>Use keys in the dictionary only once.<br /> Example input: key=foo, replacement=bar, content=foo foo.<br /> Output: bar foo (only the first foo is replaced).</li> </ul>

<strong>Demo: <a href="http://jsfiddle.net/bhGE3/3/" rel="nofollow">http://jsfiddle.net/bhGE3/3/</a></strong>


<ol><li>Define a dictionary. Each key will be used only once.</li> <li>Define content. A new string will be created, based on this string.</li> <li><em>Optionally</em>, define a replacehandler function. This function is called at each match. The return value will be used to replace the matched phrase.<br />The default replacehandler will return the dictionary's matching phrase. The function should take two arguments: key and dictionary.</li> <li>Call replaceOnceUsingDictionary(dictionary, content, replacehandler)</li> <li>Process the output, eg. show content to the user.</li> </ol><h3>Code:</h3> var dictionary = { "history": "war . ", "no": "in a", "nothing": "", "oops": "", "time": "while", "there": "We", "upon": "in", "was": "get involved" }; var content = "Once upon a time... There was no history. Nothing. Oops"; content = replaceOnceUsingDictionary(dictionary, content, function(key, dictionary){ return '_' + dictionary[key] + '_'; }); alert(content); // End of implementation /* * @name replaceOnceUsingDictionary * @author Rob W http://stackoverflow.com/users/938089/rob-w * @description Replaces phrases in a string, based on keys in a given dictionary. * Each key is used only once, and the replacements are case-insensitive * @param Object dictionary {key: phrase, ...} * @param String content * @param Function replacehandler * @returns Modified string */ function replaceOnceUsingDictionary(dictionary, content, replacehandler) { if (typeof replacehandler != "function") { // Default replacehandler function. replacehandler = function(key, dictionary){ return dictionary[key]; } } var patterns = [], // \b is used to mark boundaries "foo" doesn't match food patternHash = {}, oldkey, key, index = 0, output = []; for (key in dictionary) { // Case-insensitivity: key = (oldkey = key).toLowerCase(); dictionary[key] = dictionary[oldkey]; // Sanitize the key, and push it in the list patterns.push('\\b(?:' + key.replace(/([[^$.|?*+(){}])/g, '\\$1') + ')\\b'); // Add entry to hash variable, for an optimized backtracking at the next loop patternHash[key] = index++; } var pattern = new RegExp(patterns.join('|'), 'gi'), lastIndex = 0; // We should actually test using !== null, but for foolproofness, // we also reject empty strings while (key = pattern.exec(content)) { // Case-insensitivity key = key[0].toLowerCase(); // Add to output buffer output.push(content.substring(lastIndex, pattern.lastIndex - key.length)); // The next line is the actual replacement method output.push(replacehandler(key, dictionary)); // Update lastIndex variable lastIndex = pattern.lastIndex; // Don't match again by removing the matched word, create new pattern patterns[patternHash[key]] = '^'; pattern = new RegExp(patterns.join('|'), 'gi'); // IMPORTANT: Update lastIndex property. Otherwise, enjoy an infinite loop pattern.lastIndex = lastIndex; } output.push(content.substring(lastIndex, content.length)); return output.join(''); }


  • Use of colon in variable declaration [duplicate]
  • how much time does grid.py take to run?
  • SQL Regex get phone number
  • Is it possible to have space between cells in iTextPdf?
  • How to use i18t for texts in the source code of Angular project?
  • String encoding problem on PdoStatement->bindParam()?
  • Installing Python modules with Anaconda or Canopy
  • overlapping appointments using the entity framework
  • Is an if-let or a normal if condition better?
  • matching similar elements in between two lists
  • incomplete type 'struct' error in C
  • Swift Initialization Rule Confusion
  • KnockoutObservableArray with typed elements in TypeScript
  • SAXReader not re-ecape characters
  • Checking if an array in C is symmetric
  • C: Incompatible pointer type initializing
  • Sencha Touch 2.0 Controller refs attribute not working?
  • Checking free space on FTP server
  • When to use `image` and when to use `Matrix` in Emgu CV?
  • What is the “return” in scheme?
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • Spray.io: When (not) to use non-blocking route handling?
  • Modifying destination and filename of gulp-svg-sprite
  • Which linear programming package should I use for high numbers of constraints and “warm starts” [clo
  • Why winpcap requires both .lib and .dll to run?
  • GridView Sorting works once only
  • R: gsub and capture
  • AT Commands to Send SMS not working in Windows 8.1
  • Transpose CSV data with awk (pivot transformation)
  • How to set the response of a form post action to a iframe source?
  • Are Kotlin's Float, Int etc optimised to built-in types in the JVM? [duplicate]
  • unknown Exception android
  • Django query for large number of relationships
  • Why is Django giving me: 'first_name' is an invalid keyword argument for this function?
  • Binding checkboxes to object values in AngularJs
  • How can I use `wmic` in a Windows PE script?
  • Net Present Value in Excel for Grouped Recurring CF
  • How to push additional view controllers onto NavigationController but keep the TabBar?
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize
  • How to load view controller without button in storyboard?