2

I know the heading of this questions seems vague - but it's because I simply don't know how to summarize it appropriately.

I'm working on a project where I enter some text, and it's translated into something else.

There's a fiddle here.

If I enter 4, it translates to the word for.

If I enter b4, it should translate to before.

Instead, it translates to bfor, because it's capturing the variable 4 as a separate variable.

I've tried changing the order, but it doesn't work. Is this a regex problem?

My variables are identified in the JS.

var replaceValues = {
    '4' : 'for',
    'b4' : 'before'
}

$('.bs-text').keyup(function (event) {
    newText = event.target.value;
    for (var txt in replaceValues) {
        var temp = new RegExp(txt, 'gim');
        newText = newText.replace(temp, replaceValues[txt]);
    }
    $('.human-text').text(newText);
});
Jared Newnam
  • 2,296
  • 5
  • 31
  • 60
  • 1
    well you need to replace the bigger ones first.... – epascarello Oct 27 '17 at 17:06
  • I would suggest having an order of importance for the replacements, because depending on the circumstance, once substitution might take precedence over another. While epascarello's comment has some merit, it may not cover every specific nuance of what you are trying to accomplish. Granted, this would take more work, but I would rather have surety than brevity or ease. – Incorporeal Logic Oct 27 '17 at 17:10
  • I fixed the typo... still not working for me. **b4** shows up as **bfor**... and it should be **before**. Going back and forth on the order isn't changing anything for me. – Jared Newnam Oct 27 '17 at 17:12
  • JS object does not have defined order of items. It might be working for less object fields, but you might run into some weird situations if the dictionary will be bigger. – Martin Adámek Oct 27 '17 at 17:12
  • @MartinAdámek - the dictionary is intended to get a lot bigger. I'm hoping there's a way I don't have to create a custom regex for this. Any thoughts are greatly appreciated. – Jared Newnam Oct 27 '17 at 17:13

3 Answers3

1

As I noted in the comments, JS objects does not have defined order of its keys, so it is not a good idea to count on this when you know the dictionary will get much bigger.

More about this in another SO question: Does JavaScript Guarantee Object Property Order?

Instead, use simple array that will have the order you define. Sorting of this dictionary array can be done in JS too, you do not need to handle this by your own.

var replaceValues = [
  {key: '4', value: 'for'},
  {key: 'b4', value: 'before'},
];

// sort the values so longer keys go first
replaceValues.sort((a, b) => b.key.length - a.key.length);

$('.bs-text').keyup(function (event) {
    var newText = event.target.value;
    for (var txt in replaceValues) {
        var replacement = replaceValues[txt];
        var temp = new RegExp(replacement.key, 'gim');
        newText = newText.replace(temp, replacement.value);
    }
    $('.human-text').text(newText);
});

You could also use ES6 Map, it should have order guarantied. But be aware that it is not enough to create Map from Object:

A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.

It should be noted that a Map which is a map of an object, especially a dictionary of dictionaries, will only map to the object's insertion order—which is random and not ordered.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_and_maps_compared

Community
  • 1
  • 1
Martin Adámek
  • 12,431
  • 5
  • 25
  • 47
1

As mentioned in the comments, you have to look for the longest match first. One option is to generate a single regular expression from the search words, ordered by length, and use a callback to get the correct replacement value.

var replaceValues = {
  '4': 'for',
  'b4': 'before'
};

// generates something equivalent to `/b4|4/gmi`
var pattern = new RegExp(
  Object.keys(replaceValues)
  .sort((a, b) => b.length - a.length)
  .join('|'),
  'gmi'
);

var newText = '4 me b4 me';
console.log(newText.replace(pattern, match => replaceValues[match]));

This works because the regex engine matches alternatives from left to right (i.e. if b4 matches it won't try to match 4). Not sure how this solution scales with more searchwords, but it might actually better because you are only matching the string once instead of n times, i.e. the regex engine doesn't have to traverse the whole string multiple times.

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
0

The object property has ":" character within property value

$('.bs-text').keyup(function (event) {
    var newText = event.target.value;
    if (replaceValues[newText]) {
      $('.human-text').text(replaceValues[newText])
    };        
});

jsfiddle http://jsfiddle.net/je89deam/5/

guest271314
  • 1
  • 10
  • 82
  • 156