{"id":230,"date":"2008-07-15T16:49:29","date_gmt":"2008-07-15T20:49:29","guid":{"rendered":"http:\/\/benjamin.smedbergs.us\/blog\/?p=230"},"modified":"2008-07-15T16:49:29","modified_gmt":"2008-07-15T20:49:29","slug":"string-formatting-in-javascript","status":"publish","type":"post","link":"http:\/\/benjamin.smedbergs.us\/blog\/2008-07-15\/string-formatting-in-javascript\/","title":{"rendered":"String Formatting in JavaScript"},"content":{"rendered":"<p>I am a relative newcomer to python, and have been blown away by the flexibility of some operations in Python. The string-formatting operator, %, is really wonderful and flexible. This is my attempt at implementing something similar in JavaScript.<\/p>\n<p>Obviously, you can&#8217;t create a new operator in JavaScript, and in addition you can&#8217;t use % as a JavaScript identifier. So I went for the next-best thing:<\/p>\n<pre>String.prototype.format = function string_format(d) {\r\n  \/\/ there are two modes of operation... unnamed indices are read in order;\r\n  \/\/ named indices using %(name)s. The two styles cannot be mixed.\r\n  \/\/ Unnamed indices can be passed as either a single argument to this function,\r\n  \/\/ multiple arguments to this function, or as a single array argument\r\n  let curindex = 0;\r\n\r\n  if (arguments.length > 1)\r\n    d = arguments;\r\n  \r\n  function r(s, key, type) {\r\n    let v;\r\n    if (key == \"\") {\r\n      if (curindex == -1)\r\n        throw Error(\"Cannot mix named and positional indices in string formatting.\");\r\n\r\n      if (curindex == 0 && (!(d instanceof Object) || !(0 in d)))\r\n        v = d;\r\n      else if (!(curindex in d))\r\n        throw Error(\"Insufficient number of items in format, requesting item %i\".format(curindex));\r\n      else\r\n        v = d[curindex];\r\n\r\n      ++curindex;\r\n    }\r\n    else {\r\n      key = key.slice(1, -1);\r\n      if (curindex > 0)\r\n        throw Error(\"Cannot mix named and positional indices in string formatting.\");\r\n      curindex = -1;\r\n      \r\n      if (!(key in d))\r\n        throw Error(\"Key '%s' not present during string substitution.\".format(key));\r\n      v = d[key];\r\n    }\r\n    switch (type) {\r\n    case \"s\":\r\n      return v.toString();\r\n    case \"r\":\r\n      return v.toSource();\r\n    case \"i\":\r\n      return parseInt(v);\r\n    case \"f\":\r\n      return Number(v);\r\n    case \"%\":\r\n      return \"%\";\r\n    default:\r\n      throw Error(\"Unexpected format character '%s'.\".format(type));\r\n    }\r\n  }\r\n  return this.replace(\/%(\\([^)]+\\))?(.)\/g, r);\r\n};\r\nString.prototype.\u00c3\u00b8 = String.prototype.format;<\/pre>\n<p>If you are at all familiar with the python string-formatting operator, this should be very similar:<\/p>\n<pre>\"%s %s\".\u00c3\u00b8(\"angry\", \"monkeys\"); == \"angry monkeys\";\r\n\"%(key)s: %(value)s\".\u00c3\u00b8({key: 'bananas', value: 'tasty'}) == \"bananas: tasty\";\r\n\"%i - %i\".\u00c3\u00b8([1, 3]) == \"1 - 3\";\r\n\"%r\".\u00c3\u00b8(['\u00ce\u00b1', '\u00cf\u2030'] == '[\"\u00ce\u00b1\", \"\u00cf\u2030\"]';<\/pre>\n<p>I know that there are many sprintf-like libraries out there for JavaScript: I just happen to like mine best. Caution: this code requires JavaScript 1.7&#8230; if you replace some &#8220;let&#8221;s with &#8220;var&#8221;s it may work in older browsers, but I haven&#8217;t tested it.<\/p>\n<p>Being able to pass a function as a replacement in String.replace is a very powerful feature!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am a relative newcomer to python, and have been blown away by the flexibility of some operations in Python. The string-formatting operator, %, is really wonderful and flexible. This is my attempt at implementing something similar in JavaScript. Obviously, you can&#8217;t create a new operator in JavaScript, and in addition you can&#8217;t use % [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[130,39,129],"class_list":["post-230","post","type-post","status-publish","format-standard","hentry","category-mozilla","tag-formatting","tag-javascript","tag-string"],"_links":{"self":[{"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/posts\/230","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/comments?post=230"}],"version-history":[{"count":0,"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/posts\/230\/revisions"}],"wp:attachment":[{"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/media?parent=230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/categories?post=230"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/benjamin.smedbergs.us\/blog\/wp-json\/wp\/v2\/tags?post=230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}