mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
798 lines
22 KiB
TypeScript
798 lines
22 KiB
TypeScript
import isEqual from 'lodash/isEqual';
|
|
import random from 'lodash/random';
|
|
import uniqWith from 'lodash/uniqWith';
|
|
|
|
import type { Extension, ExtensionMap } from './extensions';
|
|
import { ExpressionExtensionError } from './expression-extension-error';
|
|
import { compact as oCompact } from './object-extensions';
|
|
|
|
// DIVERGENCE from packages/workflow/src/extensions/array-extensions.ts:
|
|
// The original uses crypto.getRandomValues() which is a Web API unavailable
|
|
// inside the V8 isolate. lodash/random (already bundled) is used instead — both
|
|
// are backed by Math.random() and randomItem() is non-security-critical.
|
|
function randomInt(max: number): number {
|
|
return random(0, max - 1);
|
|
}
|
|
|
|
// Define an own data field rather than assigning through an inherited setter.
|
|
function defineField(target: Record<string, unknown>, key: PropertyKey, value: unknown): void {
|
|
Object.defineProperty(target, key, {
|
|
value,
|
|
writable: true,
|
|
enumerable: true,
|
|
configurable: true,
|
|
});
|
|
}
|
|
|
|
// Report whether a field resolves on the object itself and not through its prototype chain.
|
|
function hasReadableField(value: object, field: string): boolean {
|
|
if (Object.hasOwn(value, field)) return true;
|
|
const proto = Object.getPrototypeOf(value) as object | null;
|
|
return field in value && (proto === null || !(field in proto));
|
|
}
|
|
|
|
function first(value: unknown[]): unknown {
|
|
return value[0];
|
|
}
|
|
|
|
function isEmpty(value: unknown[]): boolean {
|
|
return value.length === 0;
|
|
}
|
|
|
|
function isNotEmpty(value: unknown[]): boolean {
|
|
return value.length > 0;
|
|
}
|
|
|
|
function last(value: unknown[]): unknown {
|
|
return value[value.length - 1];
|
|
}
|
|
|
|
// reverse() deliberately stays copy-first: it has shadowed the native for
|
|
// years and is documented as an extension. Do NOT add other native mutator
|
|
// names here — registry names shadow natives on every receiver the expression
|
|
// transformer sees, including plain local arrays created inside expressions
|
|
// (in-place mutation on lazy-proxied workflow data is handled by the proxies
|
|
// themselves, see CAT-4266).
|
|
function reverse(value: unknown[]): unknown[] {
|
|
return [...value].reverse();
|
|
}
|
|
|
|
function pluck(value: unknown[], extraArgs: unknown[]): unknown[] {
|
|
if (!Array.isArray(extraArgs)) {
|
|
throw new ExpressionExtensionError('arguments must be passed to pluck');
|
|
}
|
|
if (!extraArgs || extraArgs.length === 0) {
|
|
return value;
|
|
}
|
|
const plucked = value.reduce<unknown[]>((pluckedFromObject, current) => {
|
|
if (current && typeof current === 'object') {
|
|
const p: unknown[] = [];
|
|
Object.keys(current).forEach((k) => {
|
|
(extraArgs as string[]).forEach((field) => {
|
|
if (current && field === k) {
|
|
p.push((current as { [key: string]: unknown })[k]);
|
|
}
|
|
});
|
|
});
|
|
if (p.length > 0) {
|
|
pluckedFromObject.push(p.length === 1 ? p[0] : p);
|
|
}
|
|
}
|
|
return pluckedFromObject;
|
|
}, new Array<unknown>());
|
|
return plucked;
|
|
}
|
|
|
|
function randomItem(value: unknown[]): unknown {
|
|
const len = value === undefined ? 0 : value.length;
|
|
return len ? value[randomInt(len)] : undefined;
|
|
}
|
|
|
|
function unique(value: unknown[], extraArgs: string[]): unknown[] {
|
|
const mapForEqualityCheck = (item: unknown): unknown => {
|
|
if (extraArgs.length > 0 && item && typeof item === 'object') {
|
|
return extraArgs.reduce<Record<string, unknown>>((acc, key) => {
|
|
const source = item as Record<string, unknown>;
|
|
defineField(acc, key, hasReadableField(source, key) ? source[key] : undefined);
|
|
return acc;
|
|
}, {});
|
|
}
|
|
return item;
|
|
};
|
|
return uniqWith(value, (a, b) => isEqual(mapForEqualityCheck(a), mapForEqualityCheck(b)));
|
|
}
|
|
|
|
const ensureNumberArray = (arr: unknown[], { fnName }: { fnName: string }) => {
|
|
if (arr.some((i) => typeof i !== 'number')) {
|
|
throw new ExpressionExtensionError(`${fnName}(): all array elements must be numbers`);
|
|
}
|
|
};
|
|
|
|
function sum(value: unknown[]): number {
|
|
ensureNumberArray(value, { fnName: 'sum' });
|
|
|
|
return value.reduce((p: number, c: unknown) => {
|
|
if (typeof c === 'string') {
|
|
return p + parseFloat(c);
|
|
}
|
|
if (typeof c !== 'number') {
|
|
return NaN;
|
|
}
|
|
return p + c;
|
|
}, 0);
|
|
}
|
|
|
|
function min(value: unknown[]): number {
|
|
ensureNumberArray(value, { fnName: 'min' });
|
|
|
|
return Math.min(
|
|
...value.map((v) => {
|
|
if (typeof v === 'string') {
|
|
return parseFloat(v);
|
|
}
|
|
if (typeof v !== 'number') {
|
|
return NaN;
|
|
}
|
|
return v;
|
|
}),
|
|
);
|
|
}
|
|
|
|
function max(value: unknown[]): number {
|
|
ensureNumberArray(value, { fnName: 'max' });
|
|
|
|
return Math.max(
|
|
...value.map((v) => {
|
|
if (typeof v === 'string') {
|
|
return parseFloat(v);
|
|
}
|
|
if (typeof v !== 'number') {
|
|
return NaN;
|
|
}
|
|
return v;
|
|
}),
|
|
);
|
|
}
|
|
|
|
export function average(value: unknown[]) {
|
|
ensureNumberArray(value, { fnName: 'average' });
|
|
|
|
// This would usually be NaN but I don't think users
|
|
// will expect that
|
|
if (value.length === 0) {
|
|
return 0;
|
|
}
|
|
return sum(value) / value.length;
|
|
}
|
|
|
|
function compact(value: unknown[]): unknown[] {
|
|
return value
|
|
.filter((v) => {
|
|
if (v && typeof v === 'object' && Object.keys(v).length === 0) return false;
|
|
|
|
return v !== null && v !== undefined && v !== 'nil' && v !== '';
|
|
})
|
|
.map((v) => {
|
|
if (typeof v === 'object' && v !== null) {
|
|
return oCompact(v);
|
|
}
|
|
return v;
|
|
});
|
|
}
|
|
|
|
function smartJoin(value: unknown[], extraArgs: string[]): object {
|
|
const [keyField, valueField] = extraArgs;
|
|
if (!keyField || !valueField || typeof keyField !== 'string' || typeof valueField !== 'string') {
|
|
throw new ExpressionExtensionError(
|
|
'smartJoin(): expected two string args, e.g. .smartJoin("name", "value")',
|
|
);
|
|
}
|
|
return value.reduce<Record<string, unknown>>((o, v) => {
|
|
if (
|
|
typeof v === 'object' &&
|
|
v !== null &&
|
|
hasReadableField(v, keyField) &&
|
|
hasReadableField(v, valueField)
|
|
) {
|
|
const entry = v as Record<string, unknown>;
|
|
defineField(o, entry[keyField] as PropertyKey, entry[valueField]);
|
|
}
|
|
return o;
|
|
}, {});
|
|
}
|
|
|
|
function chunk(value: unknown[], extraArgs: number[]) {
|
|
const [chunkSize] = extraArgs;
|
|
if (typeof chunkSize !== 'number' || chunkSize === 0) {
|
|
throw new ExpressionExtensionError('chunk(): expected non-zero numeric arg, e.g. .chunk(5)');
|
|
}
|
|
const chunks: unknown[][] = [];
|
|
for (let i = 0; i < value.length; i += chunkSize) {
|
|
chunks.push(value.slice(i, i + chunkSize));
|
|
}
|
|
return chunks;
|
|
}
|
|
|
|
function renameKeys(value: unknown[], extraArgs: string[]): unknown[] {
|
|
if (extraArgs.length === 0 || extraArgs.length % 2 !== 0) {
|
|
throw new ExpressionExtensionError(
|
|
'renameKeys(): expected an even amount of args: from1, to1 [, from2, to2, ...]. e.g. .renameKeys("name", "title")',
|
|
);
|
|
}
|
|
return value.map((v) => {
|
|
if (typeof v !== 'object' || v === null) {
|
|
return v;
|
|
}
|
|
const newObj: Record<string, unknown> = { ...(v as Record<string, unknown>) };
|
|
const chunkedArgs = chunk(extraArgs, [2]) as string[][];
|
|
chunkedArgs.forEach(([from, to]) => {
|
|
if (Object.hasOwn(newObj, from)) {
|
|
defineField(newObj, to, newObj[from]);
|
|
delete newObj[from];
|
|
}
|
|
});
|
|
return newObj;
|
|
});
|
|
}
|
|
|
|
function mergeObjects(value: Record<string, unknown>, extraArgs: unknown[]): unknown {
|
|
const [other] = extraArgs;
|
|
|
|
if (!other) {
|
|
return value;
|
|
}
|
|
|
|
if (typeof other !== 'object') {
|
|
throw new ExpressionExtensionError('merge(): expected object arg');
|
|
}
|
|
|
|
const newObject = { ...value };
|
|
for (const [key, val] of Object.entries(other)) {
|
|
if (!Object.hasOwn(newObject, key)) {
|
|
defineField(newObject, key, val);
|
|
}
|
|
}
|
|
return newObject;
|
|
}
|
|
|
|
function merge(value: unknown[], extraArgs: unknown[][]): unknown {
|
|
const [others] = extraArgs;
|
|
|
|
if (others === undefined) {
|
|
// If there are no arguments passed, merge all objects within the array
|
|
const merged = value.reduce((combined, current) => {
|
|
if (current !== null && typeof current === 'object' && !Array.isArray(current)) {
|
|
combined = mergeObjects(combined as Record<string, unknown>, [current]);
|
|
}
|
|
return combined;
|
|
}, {});
|
|
return merged;
|
|
}
|
|
|
|
if (!Array.isArray(others)) {
|
|
throw new ExpressionExtensionError(
|
|
'merge(): expected array arg, e.g. .merge([{ id: 1, otherValue: 3 }])',
|
|
);
|
|
}
|
|
const listLength = value.length > others.length ? value.length : others.length;
|
|
const merged: Record<string, unknown> = {};
|
|
for (let i = 0; i < listLength; i++) {
|
|
if (value[i] !== undefined) {
|
|
if (typeof value[i] === 'object' && typeof others[i] === 'object') {
|
|
const pair = mergeObjects(value[i] as Record<string, unknown>, [others[i]]) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
for (const [key, val] of Object.entries(pair)) {
|
|
defineField(merged, key, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
function mergeIntoObject(value: unknown[], extraArgs: unknown[][]): unknown {
|
|
const [others] = extraArgs;
|
|
|
|
if (!Array.isArray(others)) {
|
|
throw new ExpressionExtensionError(
|
|
'mergeIntoObject(): expected array arg, e.g. .mergeIntoObject([{ id: 1, otherValue: 3 }])',
|
|
);
|
|
}
|
|
const listLength = value.length > others.length ? value.length : others.length;
|
|
const merged: Record<string, unknown> = {};
|
|
for (let i = 0; i < listLength; i++) {
|
|
const baseIsObject = value[i] !== null && typeof value[i] === 'object';
|
|
const otherIsObject = others[i] !== null && typeof others[i] === 'object';
|
|
let source: Record<string, unknown> | undefined;
|
|
if (baseIsObject) {
|
|
source = mergeObjects(
|
|
value[i] as Record<string, unknown>,
|
|
otherIsObject ? [others[i]] : [],
|
|
) as Record<string, unknown>;
|
|
} else if (otherIsObject) {
|
|
source = others[i] as Record<string, unknown>;
|
|
}
|
|
if (source) {
|
|
for (const [key, val] of Object.entries(source)) {
|
|
defineField(merged, key, val);
|
|
}
|
|
}
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
function union(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
|
const [others] = extraArgs;
|
|
if (!Array.isArray(others)) {
|
|
throw new ExpressionExtensionError('union(): expected array arg, e.g. .union([1, 2, 3, 4])');
|
|
}
|
|
const newArr: unknown[] = Array.from(value);
|
|
for (const v of others) {
|
|
if (newArr.findIndex((w) => isEqual(w, v)) === -1) {
|
|
newArr.push(v);
|
|
}
|
|
}
|
|
return unique(newArr, []);
|
|
}
|
|
|
|
function difference(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
|
const [others] = extraArgs;
|
|
if (!Array.isArray(others)) {
|
|
throw new ExpressionExtensionError(
|
|
'difference(): expected array arg, e.g. .difference([1, 2, 3, 4])',
|
|
);
|
|
}
|
|
const newArr: unknown[] = [];
|
|
for (const v of value) {
|
|
if (others.findIndex((w) => isEqual(w, v)) === -1) {
|
|
newArr.push(v);
|
|
}
|
|
}
|
|
return unique(newArr, []);
|
|
}
|
|
|
|
function intersection(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
|
const [others] = extraArgs;
|
|
if (!Array.isArray(others)) {
|
|
throw new ExpressionExtensionError(
|
|
'intersection(): expected array arg, e.g. .intersection([1, 2, 3, 4])',
|
|
);
|
|
}
|
|
const newArr: unknown[] = [];
|
|
for (const v of value) {
|
|
if (others.findIndex((w) => isEqual(w, v)) !== -1) {
|
|
newArr.push(v);
|
|
}
|
|
}
|
|
for (const v of others) {
|
|
if (value.findIndex((w) => isEqual(w, v)) !== -1) {
|
|
newArr.push(v);
|
|
}
|
|
}
|
|
return unique(newArr, []);
|
|
}
|
|
|
|
function append(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
|
return value.concat(extraArgs);
|
|
}
|
|
|
|
export function toJsonString(value: unknown[]) {
|
|
return JSON.stringify(value);
|
|
}
|
|
|
|
export function toInt() {
|
|
return undefined;
|
|
}
|
|
|
|
export function toFloat() {
|
|
return undefined;
|
|
}
|
|
|
|
export function toBoolean() {
|
|
return undefined;
|
|
}
|
|
|
|
export function toDateTime() {
|
|
return undefined;
|
|
}
|
|
|
|
average.doc = {
|
|
name: 'average',
|
|
aliases: ['mean'],
|
|
description:
|
|
'Returns the average of the numbers in the array. Throws an error if there are any non-numbers.',
|
|
examples: [{ example: '[12, 1, 5].average()', evaluated: '6' }],
|
|
returnType: 'number',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-average',
|
|
};
|
|
|
|
compact.doc = {
|
|
name: 'compact',
|
|
aliases: ['removeEmpty'],
|
|
description:
|
|
'Removes any empty values from the array. <code>null</code>, <code>""</code> and <code>undefined</code> count as empty.',
|
|
examples: [{ example: '[2, null, 1, ""].compact()', evaluated: '[2, 1]' }],
|
|
returnType: 'Array',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-compact',
|
|
};
|
|
|
|
isEmpty.doc = {
|
|
name: 'isEmpty',
|
|
description: 'Returns <code>true</code> if the array has no elements or is <code>null</code>',
|
|
examples: [
|
|
{ example: '[].isEmpty()', evaluated: 'true' },
|
|
{ example: "['quick', 'brown', 'fox'].isEmpty()", evaluated: 'false' },
|
|
],
|
|
returnType: 'boolean',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-isEmpty',
|
|
};
|
|
|
|
isNotEmpty.doc = {
|
|
name: 'isNotEmpty',
|
|
description: 'Returns <code>true</code> if the array has at least one element',
|
|
examples: [
|
|
{ example: "['quick', 'brown', 'fox'].isNotEmpty()", evaluated: 'true' },
|
|
{ example: '[].isNotEmpty()', evaluated: 'false' },
|
|
],
|
|
returnType: 'boolean',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-isNotEmpty',
|
|
};
|
|
|
|
first.doc = {
|
|
name: 'first',
|
|
aliases: ['head'],
|
|
description: 'Returns the first element of the array',
|
|
examples: [{ example: "['quick', 'brown', 'fox'].first()", evaluated: "'quick'" }],
|
|
returnType: 'any',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-first',
|
|
};
|
|
|
|
last.doc = {
|
|
name: 'last',
|
|
aliases: ['tail'],
|
|
description: 'Returns the last element of the array',
|
|
examples: [{ example: "['quick', 'brown', 'fox'].last()", evaluated: "'fox'" }],
|
|
returnType: 'any',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-last',
|
|
};
|
|
|
|
max.doc = {
|
|
name: 'max',
|
|
description:
|
|
'Returns the largest number in the array. Throws an error if there are any non-numbers.',
|
|
examples: [{ example: '[1, 12, 5].max()', evaluated: '12' }],
|
|
returnType: 'number',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-max',
|
|
};
|
|
|
|
min.doc = {
|
|
name: 'min',
|
|
description:
|
|
'Returns the smallest number in the array. Throws an error if there are any non-numbers.',
|
|
examples: [{ example: '[12, 1, 5].min()', evaluated: '1' }],
|
|
returnType: 'number',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-min',
|
|
};
|
|
|
|
randomItem.doc = {
|
|
name: 'randomItem',
|
|
description: 'Returns a randomly-chosen element from the array',
|
|
examples: [
|
|
{ example: "['quick', 'brown', 'fox'].randomItem()", evaluated: "'brown'" },
|
|
{ example: "['quick', 'brown', 'fox'].randomItem()", evaluated: "'quick'" },
|
|
],
|
|
returnType: 'any',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-randomItem',
|
|
};
|
|
|
|
sum.doc = {
|
|
name: 'sum',
|
|
description:
|
|
'Returns the total of all the numbers in the array. Throws an error if there are any non-numbers.',
|
|
examples: [{ example: '[12, 1, 5].sum()', evaluated: '18' }],
|
|
returnType: 'number',
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-sum',
|
|
};
|
|
|
|
chunk.doc = {
|
|
name: 'chunk',
|
|
description: 'Splits the array into an array of sub-arrays, each with the given length',
|
|
examples: [{ example: '[1, 2, 3, 4, 5, 6].chunk(2)', evaluated: '[[1,2],[3,4],[5,6]]' }],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'length',
|
|
optional: false,
|
|
description: 'The number of elements in each chunk',
|
|
type: 'number',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-chunk',
|
|
};
|
|
|
|
difference.doc = {
|
|
name: 'difference',
|
|
description:
|
|
"Compares two arrays. Returns all elements in the base array that aren't present\nin <code>otherArray</code>.",
|
|
examples: [{ example: '[1, 2, 3].difference([2, 3])', evaluated: '[1]' }],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'otherArray',
|
|
optional: false,
|
|
description: 'The array to compare to the base array',
|
|
type: 'Array',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-difference',
|
|
};
|
|
|
|
intersection.doc = {
|
|
name: 'intersection',
|
|
description:
|
|
'Compares two arrays. Returns all elements in the base array that are also present in the other array.',
|
|
examples: [{ example: '[1, 2].intersection([2, 3])', evaluated: '[2]' }],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'otherArray',
|
|
optional: false,
|
|
description: 'The array to compare to the base array',
|
|
type: 'Array',
|
|
},
|
|
],
|
|
docURL:
|
|
'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-intersection',
|
|
};
|
|
|
|
merge.doc = {
|
|
name: 'merge',
|
|
description:
|
|
'Merges two Object-arrays into one object by merging the key-value pairs of each element.',
|
|
hidden: true,
|
|
examples: [
|
|
{
|
|
example:
|
|
"[{ name: 'Nathan' }, { age: 42 }].merge([{ city: 'Berlin' }, { country: 'Germany' }])",
|
|
evaluated: "{ name: 'Nathan', age: 42, city: 'Berlin', country: 'Germany' }",
|
|
},
|
|
],
|
|
returnType: 'Object',
|
|
args: [
|
|
{
|
|
name: 'otherArray',
|
|
optional: false,
|
|
description: 'The array to merge into the base array',
|
|
type: 'Array',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-merge',
|
|
};
|
|
|
|
mergeIntoObject.doc = {
|
|
name: 'mergeIntoObject',
|
|
description:
|
|
'Merges two Object-arrays into one object by merging the key-value pairs of each element. If the arrays have different lengths, elements from the longer array are kept.',
|
|
examples: [
|
|
{
|
|
example: "[{ name: 'Nathan' }, { age: 42 }].mergeIntoObject([{ city: 'Berlin' }])",
|
|
evaluated: "{ name: 'Nathan', age: 42, city: 'Berlin' }",
|
|
},
|
|
],
|
|
returnType: 'Object',
|
|
args: [
|
|
{
|
|
name: 'otherArray',
|
|
optional: false,
|
|
description: 'The array to merge into the base array',
|
|
type: 'Array',
|
|
},
|
|
],
|
|
docURL:
|
|
'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-mergeintoobject',
|
|
};
|
|
|
|
pluck.doc = {
|
|
name: 'pluck',
|
|
description:
|
|
"Returns an array containing the values of the given field(s) in each Object of the array. Ignores any array elements that aren't Objects or don't have a key matching the field name(s) provided.",
|
|
examples: [
|
|
{
|
|
example: "[{ name: 'Nathan', age: 42 },{ name: 'Jan', city: 'Berlin' }].pluck('name')",
|
|
evaluated: '["Nathan", "Jan"]',
|
|
},
|
|
{
|
|
example: "[{ name: 'Nathan', age: 42 },{ name: 'Jan', city: 'Berlin' }].pluck('age')",
|
|
evaluated: '[42]',
|
|
},
|
|
],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'fieldNames',
|
|
optional: false,
|
|
variadic: true,
|
|
description: 'The keys to retrieve the value of',
|
|
type: 'string',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-pluck',
|
|
};
|
|
|
|
renameKeys.doc = {
|
|
name: 'renameKeys',
|
|
description:
|
|
'Changes all matching keys (field names) of any Objects in the array. Rename more than one key by\nadding extra arguments, i.e. <code>from1, to1, from2, to2, ...</code>.',
|
|
examples: [
|
|
{
|
|
example: "[{ name: 'bob' }, { name: 'meg' }].renameKeys('name', 'x')",
|
|
evaluated: "[{ x: 'bob' }, { x: 'meg' }]",
|
|
},
|
|
],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'from',
|
|
optional: false,
|
|
description: 'The key to rename',
|
|
type: 'string',
|
|
},
|
|
{ name: 'to', optional: false, description: 'The new key name', type: 'string' },
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-renameKeys',
|
|
};
|
|
|
|
smartJoin.doc = {
|
|
name: 'smartJoin',
|
|
description:
|
|
'Creates a single Object from an array of Objects. Each Object in the array provides one field for the returned Object. Each Object in the array must contain a field with the key name and a field with the value.',
|
|
examples: [
|
|
{
|
|
example:
|
|
"[{ field: 'age', value: 2 }, { field: 'city', value: 'Berlin' }].smartJoin('field', 'value')",
|
|
evaluated: "{ age: 2, city: 'Berlin' }",
|
|
},
|
|
],
|
|
returnType: 'Object',
|
|
args: [
|
|
{
|
|
name: 'keyField',
|
|
optional: false,
|
|
description: 'The field in each Object containing the key name',
|
|
type: 'string',
|
|
},
|
|
{
|
|
name: 'nameField',
|
|
optional: false,
|
|
description: 'The field in each Object containing the value',
|
|
type: 'string',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-smartJoin',
|
|
};
|
|
|
|
union.doc = {
|
|
name: 'union',
|
|
description: 'Concatenates two arrays and then removes any duplicates',
|
|
examples: [{ example: '[1, 2].union([2, 3])', evaluated: '[1, 2, 3]' }],
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'otherArray',
|
|
optional: false,
|
|
description: 'The array to union with the base array',
|
|
type: 'Array',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-union',
|
|
};
|
|
|
|
unique.doc = {
|
|
name: 'unique',
|
|
description: 'Removes any duplicate elements from the array',
|
|
examples: [
|
|
{ example: "['quick', 'brown', 'quick'].unique()", evaluated: "['quick', 'brown']" },
|
|
{
|
|
example: "[{ name: 'Nathan', age: 42 }, { name: 'Nathan', age: 22 }].unique()",
|
|
evaluated: "[{ name: 'Nathan', age: 42 }, { name: 'Nathan', age: 22 }]",
|
|
},
|
|
{
|
|
example: "[{ name: 'Nathan', age: 42 }, { name: 'Nathan', age: 22 }].unique('name')",
|
|
evaluated: "[{ name: 'Nathan', age: 42 }]",
|
|
},
|
|
],
|
|
returnType: 'any',
|
|
aliases: ['removeDuplicates'],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-unique',
|
|
args: [
|
|
{
|
|
name: 'fieldNames',
|
|
optional: false,
|
|
variadic: true,
|
|
description: 'The object keys to check for equality',
|
|
type: 'any',
|
|
},
|
|
],
|
|
};
|
|
|
|
toJsonString.doc = {
|
|
name: 'toJsonString',
|
|
description:
|
|
"Converts the array to a JSON string. The same as JavaScript's <code>JSON.stringify()</code>.",
|
|
examples: [
|
|
{
|
|
example: "['quick', 'brown', 'fox'].toJsonString()",
|
|
evaluated: '\'["quick","brown","fox"]\'',
|
|
},
|
|
],
|
|
docURL:
|
|
'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-toJsonString',
|
|
returnType: 'string',
|
|
};
|
|
|
|
append.doc = {
|
|
name: 'append',
|
|
aliases: ['push'],
|
|
description:
|
|
'Adds new elements to the end of the array. Similar to <code>push()</code>, but returns the modified array. Consider using spread syntax instead (see examples).',
|
|
examples: [
|
|
{ example: "['forget', 'me'].append('not')", evaluated: "['forget', 'me', 'not']" },
|
|
{ example: '[9, 0, 2].append(1, 0)', evaluated: '[9, 0, 2, 1, 0]' },
|
|
{
|
|
example: '[...[9, 0, 2], 1, 0]',
|
|
evaluated: '[9, 0, 2, 1, 0]',
|
|
description: 'Consider using spread syntax instead',
|
|
},
|
|
],
|
|
docURL: 'https://docs.n8n.io/code/builtin/data-transformation-functions/arrays/#array-append',
|
|
returnType: 'Array',
|
|
args: [
|
|
{
|
|
name: 'elements',
|
|
optional: false,
|
|
variadic: true,
|
|
description: 'The elements to append, in order',
|
|
type: 'any',
|
|
},
|
|
],
|
|
};
|
|
|
|
const removeDuplicates: Extension = unique.bind({});
|
|
removeDuplicates.doc = { ...unique.doc, hidden: true };
|
|
|
|
export const arrayExtensions: ExtensionMap = {
|
|
typeName: 'Array',
|
|
functions: {
|
|
removeDuplicates,
|
|
unique,
|
|
first,
|
|
last,
|
|
reverse,
|
|
pluck,
|
|
randomItem,
|
|
sum,
|
|
min,
|
|
max,
|
|
average,
|
|
isNotEmpty,
|
|
isEmpty,
|
|
compact,
|
|
smartJoin,
|
|
chunk,
|
|
renameKeys,
|
|
merge,
|
|
mergeIntoObject,
|
|
union,
|
|
difference,
|
|
intersection,
|
|
append,
|
|
toJsonString,
|
|
toInt,
|
|
toFloat,
|
|
toBoolean,
|
|
toDateTime,
|
|
},
|
|
};
|