diff --git a/README.md b/README.md index 92e45ad..0bf6de5 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,10 @@ render("{{ last(guests) }} was last.", data); // "Patir was last." render("{{ sort([3,2,1]) }}", data); // "[1,2,3]" render("{{ sort(guests) }}", data); // "[\"Jeff\", \"Patrick\", \"Tom\"]" +// Join a list with a separator +render("{{ join([1,2,3], \" + \") }}", data); // "1 + 2 + 3" +render("{{ join(guests, \", \") }}", data); // "Jeff, Patrick, Tom" + // Round numbers to a given precision render("{{ round(3.1415, 0) }}", data); // 3 render("{{ round(3.1415, 3) }}", data); // 3.142 diff --git a/include/inja/function_storage.hpp b/include/inja/function_storage.hpp index 1b6070b..b4bd092 100644 --- a/include/inja/function_storage.hpp +++ b/include/inja/function_storage.hpp @@ -65,6 +65,7 @@ public: Sort, Upper, Super, + Join, Callback, ParenLeft, ParenRight, @@ -109,6 +110,7 @@ private: {std::make_pair("upper", 1), FunctionData { Operation::Upper }}, {std::make_pair("super", 0), FunctionData { Operation::Super }}, {std::make_pair("super", 1), FunctionData { Operation::Super }}, + {std::make_pair("join", 2), FunctionData { Operation::Join }}, }; public: diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp index d33cb46..ef951b9 100644 --- a/include/inja/renderer.hpp +++ b/include/inja/renderer.hpp @@ -528,6 +528,24 @@ class Renderer : public NodeVisitor { json_tmp_stack.push_back(result_ptr); json_eval_stack.push(result_ptr.get()); } break; + case Op::Join: { + const auto args = get_arguments<2>(node); + const auto separator = args[1]->get(); + std::ostringstream os; + std::string sep; + for (const auto& value : *args[0]) { + os << sep; + if (value.is_string()) { + os << value.get(); // otherwise the value is surrounded with "" + } else { + os << value; + } + sep = separator; + } + result_ptr = std::make_shared(os.str()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; case Op::ParenLeft: case Op::ParenRight: case Op::None: diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp index 7fca9f3..03e19a0 100644 --- a/single_include/inja/inja.hpp +++ b/single_include/inja/inja.hpp @@ -1602,6 +1602,7 @@ public: Sort, Upper, Super, + Join, Callback, ParenLeft, ParenRight, @@ -1646,6 +1647,7 @@ private: {std::make_pair("upper", 1), FunctionData { Operation::Upper }}, {std::make_pair("super", 0), FunctionData { Operation::Super }}, {std::make_pair("super", 1), FunctionData { Operation::Super }}, + {std::make_pair("join", 2), FunctionData { Operation::Join }}, }; public: @@ -4022,6 +4024,24 @@ class Renderer : public NodeVisitor { json_tmp_stack.push_back(result_ptr); json_eval_stack.push(result_ptr.get()); } break; + case Op::Join: { + const auto args = get_arguments<2>(node); + const auto separator = args[1]->get(); + std::ostringstream os; + std::string sep; + for (const auto& value : *args[0]) { + os << sep; + if (value.is_string()) { + os << value.get(); // otherwise the value is surrounded with "" + } else { + os << value; + } + sep = separator; + } + result_ptr = std::make_shared(os.str()); + json_tmp_stack.push_back(result_ptr); + json_eval_stack.push(result_ptr.get()); + } break; case Op::ParenLeft: case Op::ParenRight: case Op::None: diff --git a/test/test-functions.cpp b/test/test-functions.cpp index e0f3a6e..4ec4c25 100644 --- a/test/test-functions.cpp +++ b/test/test-functions.cpp @@ -174,6 +174,11 @@ TEST_CASE("functions") { "[inja.exception.render_error] (at 1:22) variable 'sister' not found"); } + SUBCASE("join") { + CHECK(env.render("{{ join(names, \" | \") }}", data) == "Jeff | Seb | Peter | Tom"); + CHECK(env.render("{{ join(vars, \", \") }}", data) == "2, 3, 4, 0, -1, -2, -3"); + } + SUBCASE("isType") { CHECK(env.render("{{ isBoolean(is_happy) }}", data) == "true"); CHECK(env.render("{{ isBoolean(vars) }}", data) == "false");