Алгоритм шаблонов и меток - PullRequest
4 голосов
/ 24 января 2012

Сначала краткое определение:)

  • Шаблон - Строка, которая может содержать заполнители (пример: "hello [name]")
  • Заполнитель - Подстрока, содержащая квадратные скобки (пример: "имя" в "привет [имя]:).
  • Карта свойств - Допустимый объект со строками в качестве значений

Мне нужно написать код, который заменит заполнители (вместе с скобками) на соответствующие значения в карте свойств.

пример: для следующей карты свойств:

{
    "name":"world",
    "my":"beautiful",
    "a":"[b]",
    "b":"c",
    "c":"my"
}

Ожидаемые результаты:

  • "привет имя" -> "привет имя"

  • "привет [имя]" -> "привет мир"

  • "[b]" -> "c"

  • "[a]" -> "c" (потому что [a] -> [b] -> [c])

  • "[[b]]" -> "my" (потому что [[b]] ​​-> [c] -> my)

  • "привет [мое] [имя]" -> "привет, прекрасный мир"

Ответы [ 3 ]

2 голосов
/ 24 января 2012

Ответ @chris превосходен, я просто хочу предложить альтернативное решение с использованием регулярных выражений, которое работает «наоборот», т. Е. Не ищет вхождения «версий-заполнителей» всех элементов в свойствах.map, но путем многократного поиска вхождений самого заполнителя и замены его соответствующим значением из карты свойств.У этого есть два преимущества:

  1. Если карта свойств становится очень большой, это решение должно иметь лучшую производительность (хотя все еще нужно сравнивать).
  2. Заполнитель и способ работы подстановок могутлегко изменить, отрегулировав регулярное выражение и функцию подстановки (здесь это может быть не проблема).

Недостатком, конечно, является то, что код немного сложнее (отчасти из-заДело в том, что в JavaScript отсутствует хороший способ подстановки совпадений регулярных выражений с использованием пользовательских функций, поэтому substituteRegExp предназначен для этого:

function substituteRegExp(string, regexp, f) {
    // substitute all matches of regexp in string with the value
    // returned by f given a match and the corresponding group values
    var found;
    var lastIndex = 0;
    var result = "";
    while (found = regexp.exec(string)) {
        var subst = f.apply(this, found);
        result += string.slice(lastIndex, found.index) + subst;
        lastIndex = found.index + found[0].length;
    }
    result += string.slice(lastIndex);
    return result;
}

function templateReplace(string, values) {
    // repeatedly substitute [key] placeholders in string by values[key]
    var placeholder = /\[([a-zA-Z0-9]+)\]/g;
    while (true) {
        var newString = substituteRegExp(string, placeholder, function(match, key) {
            return values[key];
        });
        if (newString == string)
            break;
        string = newString;
    }
    return string;
}

alert(templateReplace("hello [[b]] [my] [name]", {
    "name":"world",
    "my":"beautiful",
    "a":"[b]",
    "b":"c",
    "c":"my"
})); // -> "hello my beautiful world"

Обновление : я провел небольшое профилирование для сравнениядва решения (jsFiddle на http://jsfiddle.net/n8Fyv/1/, Я также использовал Firebug).Хотя решение @chris быстрее для небольших строк (нет необходимости разбирать регулярное выражение и т. Д.), Это решение работает намного лучше для больших строк (порядка тысяч символов).Я не сравнивал различные размеры карты свойств, но ожидал еще больших различий.

Теоретически это решение имеет время выполнения O ( k n ), где k - глубина вложения заполнителей, а n - длина строки (при условии, что для поиска в словаре / хэше требуется постоянное время), в то время как решение @chris равно O (k n m ) где m - количество объектов на карте свойств.Разумеется, все это относится только к крупным входам.

2 голосов
/ 24 января 2012
var map = {
    "name":"world",
    "my":"beautiful",
    "a":"[b]",
    "b":"c",
    "c":"my"
};

var str = "hello [my] [name] [[b]]";

do {
    var strBeforeReplace = str;
    for (var k in map) {
        if (!map.hasOwnProperty(k)) continue;
        var needle = "[" + k + "]";
        str = str.replace(needle, map[k]);
    }
    var strChanged = str !== strBeforeReplace;
} while (strChanged);

document.write(str); //hello beautiful world my
0 голосов
/ 24 января 2012

Если вы знакомы с .NET String.Format, вам следует взглянуть на эту реализацию JavaScript . Он также поддерживает форматирование чисел, как String.Format.

Вот пример того, как его использовать:

var result = String.Format("Hello {my} {name}", map);

Однако для создания рекурсивных шаблонов потребуется некоторая модификация.

...