aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2021-12-02 17:46:44 +0100
committerSilvan Mosberger <contact@infinisil.com>2021-12-02 21:54:51 +0100
commit90700736c7744b97ab69a4fadcd56fa242ec617f (patch)
treefe1915e61e4d367dd393fc4c45021c9fcf7ac226 /src
parent2ff71b021379a2c9bbdcb789a93cdc585b3520ca (diff)
Introduce builtins.groupBy primop
This function is very useful in nixpkgs, but its implementation in Nix itself is rather slow due to it requiring a lot of attribute set and list appends.
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/primops.cc50
-rw-r--r--src/libexpr/value.hh2
2 files changed, 52 insertions, 0 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index d1f4d9009..66af373d7 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -2928,6 +2928,56 @@ static RegisterPrimOp primop_partition({
.fun = prim_partition,
});
+static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ state.forceFunction(*args[0], pos);
+ state.forceList(*args[1], pos);
+
+ ValueVectorMap attrs;
+
+ for (auto vElem : args[1]->listItems()) {
+ Value res;
+ state.callFunction(*args[0], *vElem, res, pos);
+ string name = state.forceStringNoCtx(res, pos);
+ Symbol sym = state.symbols.create(name);
+ auto vector = attrs.try_emplace(sym, ValueVector()).first;
+ vector->second.push_back(vElem);
+ }
+
+ state.mkAttrs(v, attrs.size());
+
+ for (auto & i : attrs) {
+ Value * list = state.allocAttr(v, i.first);
+ auto size = i.second.size();
+ state.mkList(*list, size);
+ memcpy(list->listElems(), i.second.data(), sizeof(Value *) * size);
+ }
+}
+
+static RegisterPrimOp primop_groupBy({
+ .name = "__groupBy",
+ .args = {"f", "list"},
+ .doc = R"(
+ Groups elements of *list* together by the string returned from the
+ function *f* called on each element. It returns an attribute set
+ where each attribute value contains the elements of *list* that are
+ mapped to the same corresponding attribute name returned by *f*.
+
+ For example,
+
+ ```nix
+ builtins.groupBy (builtins.substring 0 1) ["foo" "bar" "baz"]
+ ```
+
+ evaluates to
+
+ ```nix
+ { b = [ "bar" "baz" ]; f = [ "foo" ]; }
+ ```
+ )",
+ .fun = prim_groupBy,
+});
+
static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 4b43e47ae..6b4f3c0ae 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -425,9 +425,11 @@ void mkPath(Value & v, const char * s);
#if HAVE_BOEHMGC
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
+typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
#else
typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap;
+typedef std::map<Symbol, ValueVector> ValueVectorMap;
#endif