diff --git a/doc/19-library-reference.md b/doc/19-library-reference.md index 3d1f13654..99664fb03 100644 --- a/doc/19-library-reference.md +++ b/doc/19-library-reference.md @@ -692,6 +692,43 @@ Signature: Returns a new array with all elements of the current array in reverse order. +### Array#map + +Signature: + + function map(func); + +Calls `func(element)` for each of the elements in the array and returns +a new array containing the return values of these function calls. + +### Array#reduce + +Signature: + + function reduce(func); + +Reduces the elements of the array into a single value by calling the provided +function `func` as `func(a, b)` repeatedly where `a` is the previous result of +function call (null initially) and `b` is an element of the array. + +### Array#filter + +Signature: + + function filter(func); + +Returns a copy of the array containing only the elements for which `func(element)` +is true. + +### Array#unique + +Signature: + + function unique(); + +Returns a copy of the array with all duplicate elements removed. The original order +of the array is not preserved. + ## Dictionary type Inherits methods from the [Object type](19-library-reference.md#object-type). diff --git a/lib/base/array-script.cpp b/lib/base/array-script.cpp index c8e4731e2..c0482312a 100644 --- a/lib/base/array-script.cpp +++ b/lib/base/array-script.cpp @@ -22,6 +22,7 @@ #include "base/functionwrapper.hpp" #include "base/scriptframe.hpp" #include "base/objectlock.hpp" +#include "base/exception.hpp" #include using namespace icinga; @@ -94,6 +95,11 @@ static Array::Ptr ArraySort(const std::vector& args) ObjectLock olock(arr); std::sort(arr->Begin(), arr->End()); } else { + Function::Ptr function = args[0]; + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Sort function must be side-effect free.")); + ObjectLock olock(arr); std::sort(arr->Begin(), arr->End(), boost::bind(ArraySortCmp, args[0], _1, _2)); } @@ -137,6 +143,86 @@ static Array::Ptr ArrayReverse(void) return self->Reverse(); } +static Array::Ptr ArrayMap(const Function::Ptr& function) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Map function must be side-effect free.")); + + Array::Ptr result = new Array(); + + ObjectLock olock(self); + BOOST_FOREACH(const Value& item, self) { + ScriptFrame uframe; + std::vector args; + args.push_back(item); + result->Add(function->Invoke(args)); + } + + return result; +} + +static Value ArrayReduce(const Function::Ptr& function) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Reduce function must be side-effect free.")); + + Value result; + + ObjectLock olock(self); + BOOST_FOREACH(const Value& item, self) { + ScriptFrame uframe; + std::vector args; + args.push_back(result); + args.push_back(item); + result = function->Invoke(args); + } + + return result; +} + +static Array::Ptr ArrayFilter(const Function::Ptr& function) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + if (vframe->Sandboxed && !function->IsSideEffectFree()) + BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free.")); + + Array::Ptr result = new Array(); + + ObjectLock olock(self); + BOOST_FOREACH(const Value& item, self) { + ScriptFrame uframe; + std::vector args; + args.push_back(item); + if (function->Invoke(args)) + result->Add(item); + } + + return result; +} + +static Array::Ptr ArrayUnique(void) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Array::Ptr self = static_cast(vframe->Self); + + std::set result; + + ObjectLock olock(self); + BOOST_FOREACH(const Value& item, self) { + result.insert(item); + } + + return Array::FromSet(result); +} + Object::Ptr Array::GetPrototype(void) { static Dictionary::Ptr prototype; @@ -154,6 +240,10 @@ Object::Ptr Array::GetPrototype(void) prototype->Set("shallow_clone", new Function(WrapFunction(ArrayShallowClone), true)); prototype->Set("join", new Function(WrapFunction(ArrayJoin), true)); prototype->Set("reverse", new Function(WrapFunction(ArrayReverse), true)); + prototype->Set("map", new Function(WrapFunction(ArrayMap), true)); + prototype->Set("reduce", new Function(WrapFunction(ArrayReduce), true)); + prototype->Set("filter", new Function(WrapFunction(ArrayFilter), true)); + prototype->Set("unique", new Function(WrapFunction(ArrayUnique), true)); } return prototype; diff --git a/lib/base/array.hpp b/lib/base/array.hpp index 5981ab01e..0d50be8f2 100644 --- a/lib/base/array.hpp +++ b/lib/base/array.hpp @@ -118,6 +118,15 @@ public: return std::set(Begin(), End()); } + template + static Array::Ptr FromSet(const std::set& v) + { + Array::Ptr result = new Array(); + ObjectLock olock(result); + std::copy(v.begin(), v.end(), std::back_inserter(result->m_Data)); + return result; + } + virtual Object::Ptr Clone(void) const override; Array::Ptr Reverse(void) const;