diff --git a/nix-js/runtime-ts/src/builtins/attrs.ts b/nix-js/runtime-ts/src/builtins/attrs.ts index 4484b45..01d7c09 100644 --- a/nix-js/runtime-ts/src/builtins/attrs.ts +++ b/nix-js/runtime-ts/src/builtins/attrs.ts @@ -107,3 +107,36 @@ export const groupBy = } return attrs; }; + +export const zipAttrsWith = + (f: NixValue) => + (list: NixValue): NixValue => { + const listForced = forceList(list); + + // Map to collect all values for each attribute name + const attrMap = new Map(); + + // Iterate through each attribute set in the list + for (const item of listForced) { + const attrs = forceAttrs(item); + + // Collect all attribute names and their values + for (const [key, value] of Object.entries(attrs)) { + if (!attrMap.has(key)) { + attrMap.set(key, []); + } + attrMap.get(key)!.push(value); + } + } + + // Build the result attribute set + const result: Record = {}; + + for (const [name, values] of attrMap.entries()) { + // Apply f to name and values list + // f is curried: f name values + result[name] = createThunk(() => forceFunction(forceFunction(f)(name))(values)); + } + + return result; + }; diff --git a/nix-js/runtime-ts/src/builtins/index.ts b/nix-js/runtime-ts/src/builtins/index.ts index 3da9bb9..abe334c 100644 --- a/nix-js/runtime-ts/src/builtins/index.ts +++ b/nix-js/runtime-ts/src/builtins/index.ts @@ -172,6 +172,7 @@ export const builtins: any = { intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2), catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2), groupBy: mkPrimop(attrs.groupBy, "groupBy", 2), + zipAttrsWith: mkPrimop(attrs.zipAttrsWith, "zipAttrsWith", 2), stringLength: mkPrimop(string.stringLength, "stringLength", 1), substring: mkPrimop(string.substring, "substring", 3), @@ -247,7 +248,6 @@ export const builtins: any = { splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1), traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2), tryEval: mkPrimop(misc.tryEval, "tryEval", 1), - zipAttrsWith: mkPrimop(misc.zipAttrsWith, "zipAttrsWith", 2), builtins: createThunk(() => builtins, "builtins"), currentSystem: createThunk(() => { diff --git a/nix-js/runtime-ts/src/builtins/misc.ts b/nix-js/runtime-ts/src/builtins/misc.ts index adda38d..d8ea458 100644 --- a/nix-js/runtime-ts/src/builtins/misc.ts +++ b/nix-js/runtime-ts/src/builtins/misc.ts @@ -2,7 +2,7 @@ * Miscellaneous builtin functions */ -import { force } from "../thunk"; +import { createThunk, force } from "../thunk"; import { CatchableError } from "../types"; import type { NixAttrs, NixBool, NixStrictValue, NixValue } from "../types"; import { forceList, forceAttrs, forceFunction, forceStringValue, forceString } from "../type-assert"; @@ -355,37 +355,3 @@ export const tryEval = (e: NixValue): { success: NixBool; value: NixStrictValue } } }; - -export const zipAttrsWith = - (f: NixValue) => - (list: NixValue): NixValue => { - const listForced = forceList(list); - - // Map to collect all values for each attribute name - const attrMap = new Map(); - - // Iterate through each attribute set in the list - for (const item of listForced) { - const attrs = forceAttrs(force(item) as NixValue); - - // Collect all attribute names and their values - for (const [key, value] of Object.entries(attrs)) { - if (!attrMap.has(key)) { - attrMap.set(key, []); - } - attrMap.get(key)!.push(value); - } - } - - // Build the result attribute set - const result: Record = {}; - - for (const [name, values] of attrMap.entries()) { - // Apply f to name and values list - // f is curried: f name values - const fWithName = forceFunction(f)(name); - result[name] = forceFunction(fWithName)(values); - } - - return result; - };