Sformatować ciąg JavaScript przy użyciu symboli zastępczych i obiektu podstawień?

92

Mam ciąg z powiedz: My Name is %NAME% and my age is %AGE%.

%XXX%są symbolami zastępczymi. Musimy tam podstawić wartości z obiektu.

Obiekt wygląda następująco: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

Muszę przeanalizować obiekt i zamienić ciąg na odpowiednie wartości. Tak więc ostateczny wynik będzie:

Nazywam się Michał i mam 26 lat.

Całość trzeba zrobić używając czystego javascript lub jquery.

Joby Joseph
źródło
2
To wygląda bardziej jak obiekt niż tablica
Joel Coehoorn
3
Czego spróbowałeś do tej pory? Czy spojrzałeś na metodę string .replace () ? (Poza tym nie masz tam tablicy, masz obiekt.)
nnnnnn
1
To dość brzydkie. Z pewnością byłbyś równie dobrze obsługiwany przez {NAME: "Mike", AGE: 26, EVENT: 20}? Oczywiście nadal wymagałbyś, aby te klucze były oznaczone znakami procentu w ciągu wejściowym.
davidchambers

Odpowiedzi:

152

Interpolacja ciągów najwyraźniej nie przyniosła korzyści wymaganiom pierwotnego pytania, ponieważ wydaje się, że jest to przetwarzanie w czasie wykonywania dowolnych kluczy zastępczych.

Jeśli jednak musiałeś tylko wykonać interpolację ciągów, możesz użyć:

const str = `My name is ${replacements.name} and my age is ${replacements.age}.`

Zwróć uwagę na znaki odwrotne ograniczające ciąg, są one wymagane.


Aby uzyskać odpowiedź odpowiadającą wymaganiom konkretnego OP, możesz użyć String.prototype.replace()do zamiany.

Poniższy kod obsłuży wszystkie dopasowania i nie zmieni tych bez zamiany (o ile wszystkie wartości zastępcze są ciągami, jeśli nie, patrz poniżej).

var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
    str = 'My Name is %NAME% and my age is %AGE%.';

str = str.replace(/%\w+%/g, function(all) {
   return replacements[all] || all;
});

jsFiddle .

Jeśli niektóre zamienniki nie są łańcuchami, upewnij się najpierw, że istnieją w obiekcie. Jeśli masz format taki jak na przykładzie, tj. Zawinięty w znaki procentu, możesz użyć inoperatora, aby to osiągnąć.

jsFiddle .

Jednakże, jeśli twój format nie ma specjalnego formatu, tj. Żadnego łańcucha, a obiekt zamienników nie ma nullprototypu, użyj Object.prototype.hasOwnProperty(), chyba że możesz zagwarantować, że żaden z twoich potencjalnych zastępowanych podciągów nie będzie kolidował z nazwami właściwości w prototypie.

jsFiddle .

W przeciwnym razie, jeśli twój ciąg zastępujący był 'hasOwnProperty', otrzymasz wynikowy zepsuty ciąg.

jsFiddle .


Na marginesie, powinieneś nazywać replacementssięObject , a nie Array.

Alex
źródło
2
+1. Ładny. Chociaż możesz chcieć powiedzieć, return replacements[all] || allaby objąć %NotInReplacementsList%sprawy.
nnnnnn
6
To nie zadziała, jeśli wartość zastąpienia jest fałszywa. Więc lepiej użyć tego zwrotu:return all in params ? params[all] : all;
Michael Härtl
@ MichaelHärtl Czy twoje zamienniki nie powinny być strunami? Jeśli chcesz zastąpić ciągiem pustym, lepiej sprawdź za pomocą innych środków.
Alex
1
@alex Miałem sytuację, w której zamienniki mogły być również liczbami całkowitymi, a nawet 0. W tym przypadku to nie zadziałało.
Michael Härtl
@ MichaelHärtl Zaktualizowano, aby uwzględnić tę sprawę.
alex
24

Co powiesz na użycie literałów szablonów ES6?

var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string

Więcej o literałach szablonów ...

Tomasz Mularczyk
źródło
5
Jeśli masz obiekt z symbolami zastępczymi, takimi jak OP, w jaki sposób pomaga w tym interpolacja ciągów?
Alex
Działa jak marzenie! Najbardziej pragmatyczne rozwiązanie moich problemów z utrzymaniem miejsca, idealne.
BAERUS
To rozwiązanie nie działa w przypadku zamienników środowiska uruchomieniowego
Thierry J.
13

Możesz użyć JQuery (jquery.validate.js), aby łatwo działało.

$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);

Lub jeśli chcesz używać tylko tej funkcji, możesz zdefiniować tę funkcję i po prostu jej używać

function format(source, params) {
    $.each(params,function (i, n) {
        source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
    })
    return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));

Podziękowania dla zespołu jquery.validate.js

zawhtut
źródło
1
Na pewno nie chciałbyś ładować tej wtyczki tylko w tym celu, ale już używam tego do walidacji formularza na stronie ... więc dzięki za wskazówkę!
nabrown
1
Tworzenie wyrażenia regularnego dla każdej liczby byłoby dość nieefektywne, lepiej byłoby dopasować wszystkie liczby, a następnie zamienić na, jeśli wartość została znaleziona w tablicy, być może?
Alex
dla zainteresowanych dzieje się to tutaj: github.com/jquery-validation/jquery-validation/blob/master/src/…
Raphael C
1
+ bardzo fajne ... i dla $.eachciebie możesz sprawić String.prototype.format=function(p){var s=this,r=function(v,i){s=s.replace(new RegExp("\\{"+i+"\\}","g"),v);};p.forEach(r);return s;}, że nie musisz dołączać jquery tylko do tego;)
Larphoid
11

Jak z nowoczesnej przeglądarki, zastępczy jest obsługiwany przez nową wersją Chrome / Firefox, podobnie jak funkcja C stylu printf().

Symbole zastępcze:

  • %s Strunowy.
  • %d, Liczba %icałkowita.
  • %f Liczba zmiennoprzecinkowa.
  • %o Hiperłącze do obiektu.

na przykład

console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);

BTW, aby zobaczyć wyjście:

  • W przeglądarce Chrome użyj skrótu Ctrl + Shift + Jlub F12otwórz narzędzie programistyczne.
  • W przeglądarce Firefox użyj skrótu Ctrl + Shift + Klub F12otwórz narzędzie programistyczne.

@Update - wsparcie nodejs

Wygląda na to, że nodejs nie obsługuje %f, zamiast tego można użyć %dw nodejs. Z %dliczby zostaną wydrukowane jako pływającą liczby, a nie tylko całkowitą.

Eric Wang
źródło
5

Możesz użyć niestandardowej funkcji zamiany, takiej jak ta:

var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};

function substitute(str, data) {
    var output = str.replace(/%[^%]+%/g, function(match) {
        if (match in data) {
            return(data[match]);
        } else {
            return("");
        }
    });
    return(output);
}

var output = substitute(str, replaceData);

Możesz zobaczyć, jak działa tutaj: http://jsfiddle.net/jfriend00/DyCwk/ .

jfriend00
źródło
1
Super, Alex zrobił prawie to samo, ale w mniejszej liczbie wierszy kodu (chociaż operatory trójskładnikowe są prawdopodobnie wolniejsze niż if..else).
RobG
Hej, dałem Ci +1! Oboje wykonaliście funkcję zamiany, twoja nie jest dokładnie taka sama, ale dość podobna. Twoje wyrażenie RegExp też jest inne, w OP lepiej byłoby użyć %% lub $$ lub podobnych jako separatorów - pojedynczy% lub% zwykle występuje w ciągu, ale podwojenie są mało prawdopodobne.
RobG
4

Jeśli chcesz zrobić coś bliższego console.log, na przykład zastąpić symbole zastępcze% s, takie jak w

>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?

ja to napisałem

function log() {
  var args = Array.prototype.slice.call(arguments);
  var rep= args.slice(1, args.length);
  var i=0;
  var output = args[0].replace(/%s/g, function(match,idx) {
    var subst=rep.slice(i, ++i);
    return( subst );
  });
   return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>

dostaniesz

>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"

AKTUALIZACJA

Dodałem prosty wariant, String.prototypeprzydatny przy przetwarzaniu ciągów, oto on:

String.prototype.log = function() {
    var args = Array.prototype.slice.call(arguments);
    var rep= args.slice(0, args.length);
    var i=0;
    var output = this.replace(/%s|%d|%f|%@/g, function(match,idx) {
      var subst=rep.slice(i, ++i);
      return( subst );
    });
    return output;
   }

W takim razie zrobisz

"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"

Wypróbuj tę wersję tutaj

loretoparisi
źródło
2
Zrobiłem wariację na temat twojej funkcji bez prototypów, która formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } przenosi wiadomość do formatowania i tablicy wartości zastępczych i szuka%SOME_VALUE%
baku
4

Obecnie w Javascript nadal nie ma natywnego rozwiązania dla tego zachowania. Otagowane szablony są czymś powiązanym, ale nie rozwiązuj tego.

Tutaj jest refaktor rozwiązania Alexa z obiektem do wymiany.

Rozwiązanie wykorzystuje funkcje strzałkowe i podobną składnię dla symboli zastępczych, jak natywna interpolacja JavaScript w literałach szablonów ( {}zamiast %%). Nie ma też potrzeby dołączania ograniczników (% ) w nazwach zamienników.

Istnieją dwa smaki: opisowy i zredukowany.

Rozwiązanie opisowe:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(
  /{\w+}/g,
  placeholderWithDelimiters => {
    const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
      1,
      placeholderWithDelimiters.length - 1,
    );
    const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
    return stringReplacement;
  },
);

console.log(string);

Rozwiązanie zredukowane:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
  replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder,
);

console.log(string);

AMS777
źródło
1
Dzięki za to wspaniale jest mieć ogólne rozwiązanie dla ciągu znaków zastępczych i obiektu do scalenia z nim w ten sposób
Carlos P
@CarlosP, taki jest pomysł, aby mieć ogólne rozwiązanie. Ale myślę, że powinno to być coś rodzimego dla języka, ponieważ jest to powszechny przypadek użycia.
AMS777
1
Można to nieco bardziej uprościć, używając grup wyrażeń regularnych, takich jak ta:stringWithPlaceholders.replace(/{(\w+)}/g, (fullMatch, group1) => replacements[group1] || fullMatch )
Kade
2

To pozwala ci to zrobić

NPM: https://www.npmjs.com/package/stringinject

GitHub: https://github.com/tjcafferkey/stringinject

Wykonując następujące czynności:

var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });

// My username is tjcafferkey on Git
tjcafferkey
źródło
2
Ale skoro możesz to zrobić w es6, to prawdopodobnie trochę przesada?
IonicBurger
1
Umożliwia to przechowywanie ciągu w jednym miejscu w jednym formacie, a później zastąpienie odpowiednich elementów przy użyciu tylko jednej funkcji. O ile mi wiadomo, nie jest to możliwe w przypadku literałów szablonów es6. Można do tego użyć np. Tłumaczeń tekstowych, w których użyjesz ciągu w innym miejscu i wstrzykniesz tam żądane wartości.
Johan Persson
2

Oto inny sposób na zrobienie tego poprzez dynamiczne używanie literałów szablonu es6 w czasie wykonywania.

const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}


const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})

document.body.innerHTML = result

Simon Schärer
źródło
1

Jako szybki przykład:

var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);

Wynik to:

Jack ma 40 lat

Yomi
źródło
7
To świetnie, chyba że chcesz coś zrobić poza zalogowaniem się do konsoli.
alex
13
nie powinno tak być console.log?
drzaus
0
const stringInject = (str = '', obj = {}) => {
  let newStr = str;
  Object.keys(obj).forEach((key) => {
    let placeHolder = `#${key}#`;
    if(newStr.includes(placeHolder)) {
      newStr = newStr.replace(placeHolder, obj[key] || " ");
    }
  });
  return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"
Shubham Vyas
źródło
0

Napisałem kod, który umożliwia łatwe formatowanie ciągu znaków.

Użyj tej funkcji.

function format() {
    if (arguments.length === 0) {
        throw "No arguments";
    }
    const string = arguments[0];
    const lst = string.split("{}");
    if (lst.length !== arguments.length) {
        throw "Placeholder format mismatched";
    }
    let string2 = "";
    let off = 1;
    for (let i = 0; i < lst.length; i++) {
        if (off < arguments.length) {
            string2 += lst[i] + arguments[off++]
        } else {
            string2 += lst[i]
        }
    }
    return string2;
}

Przykład

format('My Name is {} and my age is {}', 'Mike', 26);

Wynik

Nazywam się Michał i mam 26 lat

bikram
źródło