Map object keys and values into a new object
npx @tessl/cli install tessl/npm-map-obj@5.0.0Map Object provides a utility function for mapping object keys and values into new objects, supporting both shallow and deep transformation of nested structures. It offers a flexible mapper function that allows developers to rename keys, transform values, and selectively exclude properties using a special skip symbol, with built-in support for recursive processing of nested objects and arrays while maintaining proper circular reference handling.
npm install map-objimport mapObject, { mapObjectSkip } from "map-obj";For CommonJS:
const mapObject = require("map-obj");
const { mapObjectSkip } = require("map-obj");import mapObject, { mapObjectSkip } from "map-obj";
// Transform keys and values
const result = mapObject({ foo: "bar" }, (key, value) => [value, key]);
// Result: { bar: "foo" }
// Transform keys (e.g., normalize case)
const normalized = mapObject(
{ FOO: true, bAr: { bAz: true } },
(key, value) => [key.toLowerCase(), value]
);
// Result: { foo: true, bar: { bAz: true } }
// Deep transformation
const deepNormalized = mapObject(
{ FOO: true, bAr: { bAz: true } },
(key, value) => [key.toLowerCase(), value],
{ deep: true }
);
// Result: { foo: true, bar: { baz: true } }
// Exclude properties using skip symbol
const filtered = mapObject(
{ one: 1, two: 2 },
(key, value) => (value === 1 ? [key, value] : mapObjectSkip)
);
// Result: { one: 1 }Maps object keys and values into a new object using a provided mapper function.
/**
* Map object keys and values into a new object
* @param source - The source object to copy properties from
* @param mapper - A mapping function that transforms keys and values
* @param options - Optional configuration for mapping behavior
* @returns New object with mapped keys and values
*/
function mapObject<
SourceObjectType extends Record<string, unknown>,
TargetObjectType extends Record<string, any>,
MappedObjectKeyType extends string,
MappedObjectValueType,
>(
source: SourceObjectType,
mapper: Mapper<SourceObjectType, MappedObjectKeyType, MappedObjectValueType>,
options: DeepOptions & TargetOptions<TargetObjectType>
): TargetObjectType & Record<string, unknown>;
function mapObject<
SourceObjectType extends Record<string, unknown>,
MappedObjectKeyType extends string,
MappedObjectValueType,
>(
source: SourceObjectType,
mapper: Mapper<SourceObjectType, MappedObjectKeyType, MappedObjectValueType>,
options: DeepOptions
): Record<string, unknown>;
function mapObject<
SourceObjectType extends Record<string, any>,
TargetObjectType extends Record<string, any>,
MappedObjectKeyType extends string,
MappedObjectValueType,
>(
source: SourceObjectType,
mapper: Mapper<SourceObjectType, MappedObjectKeyType, MappedObjectValueType>,
options: TargetOptions<TargetObjectType>
): TargetObjectType & {[K in MappedObjectKeyType]: MappedObjectValueType};
function mapObject<
SourceObjectType extends Record<string, any>,
MappedObjectKeyType extends string,
MappedObjectValueType,
>(
source: SourceObjectType,
mapper: Mapper<SourceObjectType, MappedObjectKeyType, MappedObjectValueType>,
options?: Options
): {[K in MappedObjectKeyType]: MappedObjectValueType};Usage Examples:
// Key transformation
const camelCased = mapObject(
{ "first-name": "John", "last-name": "Doe" },
(key, value) => [key.replace(/-(\w)/g, (_, char) => char.toUpperCase()), value]
);
// Result: { firstName: "John", lastName: "Doe" }
// Value transformation
const doubled = mapObject(
{ a: 1, b: 2, c: 3 },
(key, value) => [key, value * 2]
);
// Result: { a: 2, b: 4, c: 6 }
// Using target option
const target = { existing: "value" };
const merged = mapObject(
{ new: "data" },
(key, value) => [key, value],
{ target }
);
// Result: target object is modified and returnedSpecial symbol returned from a mapper function to exclude a key from the result object.
/**
* Return this value from a mapper function to remove a key from an object
*/
const mapObjectSkip: unique symbol;Usage Examples:
import mapObject, { mapObjectSkip } from "map-obj";
// Conditional inclusion
const adults = mapObject(
{ alice: 25, bob: 16, charlie: 30 },
(key, value) => (value >= 18 ? [key, value] : mapObjectSkip)
);
// Result: { alice: 25, charlie: 30 }
// Property filtering based on key patterns
const publicProps = mapObject(
{ name: "John", _private: "secret", age: 30, _id: 123 },
(key, value) => (key.startsWith("_") ? mapObjectSkip : [key, value])
);
// Result: { name: "John", age: 30 }type Mapper<
SourceObjectType extends Record<string, any>,
MappedObjectKeyType extends string,
MappedObjectValueType,
> = (
sourceKey: keyof SourceObjectType,
sourceValue: SourceObjectType[keyof SourceObjectType],
source: SourceObjectType
) => [
targetKey: MappedObjectKeyType,
targetValue: MappedObjectValueType,
mapperOptions?: MapperOptions,
] | typeof mapObjectSkip;interface Options {
/**
* Recurse nested objects and objects in arrays
* @default false
*/
readonly deep?: boolean;
/**
* The target object to map properties on to
* @default {}
*/
readonly target?: Record<string, any>;
}interface DeepOptions extends Options {
readonly deep: true;
}interface TargetOptions<TargetObjectType extends Record<string, any>> extends Options {
readonly target: TargetObjectType;
}interface MapperOptions {
/**
* Whether targetValue should be recursed
* Requires deep: true
* @default true
*/
readonly shouldRecurse?: boolean;
}const nested = {
user: {
profile: {
firstName: "John",
lastName: "Doe"
},
preferences: ["email", "sms"]
}
};
const normalized = mapObject(
nested,
(key, value) => [key.toLowerCase(), value],
{ deep: true }
);
// All nested keys are transformed recursivelyconst data = {
metadata: { version: "1.0", author: "John" },
content: { title: "Example", body: "Content here" }
};
const result = mapObject(
data,
(key, value) => {
if (key === "metadata") {
// Don't recurse into metadata
return [key, value, { shouldRecurse: false }];
}
return [key.toUpperCase(), value];
},
{ deep: true }
);
// metadata object is not recursively transformedconst obj = { name: "test" };
obj.self = obj; // Create circular reference
const mapped = mapObject(
obj,
(key, value) => [key.toUpperCase(), value],
{ deep: true }
);
// Circular references are handled automatically using WeakMap trackingThe mapObject function validates its input and throws TypeError in the following cases:
TypeError: Expected an object, got \value` (type)`TypeError: Expected an object, got an arraytry {
mapObject(null, (k, v) => [k, v]);
} catch (error) {
console.log(error.message); // "Expected an object, got `null` (object)"
}
try {
mapObject([1, 2, 3], (k, v) => [k, v]);
} catch (error) {
console.log(error.message); // "Expected an object, got an array"
}