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;