flattener-kit
Version:
An object flatten/unflatten utility with OOP approach and transform support.
143 lines • 5.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Flattener = void 0;
class Flattener {
payload;
defaultFlattenConfig = {
delimiter: '.',
safe: false,
safes: [],
depth: Infinity,
transformKey: (key) => key,
};
defaultUnflattenConfig = {
delimiter: '.',
overwrite: false,
object: false,
transformKey: (key) => key,
};
constructor(payload) {
this.payload = payload;
}
flatten(config = {}) {
const finalConfig = { ...this.defaultFlattenConfig, ...config };
return Flattener._flatten(this.payload, finalConfig);
}
unflatten(config = {}) {
const finalConfig = { ...this.defaultUnflattenConfig, ...config };
return Flattener._unflatten(this.payload, finalConfig);
}
static _flatten(payload, config, prefix = '', step = 0) {
const { delimiter, safes, safe, depth, transformKey } = config;
const result = {};
if (prefix && safes.includes(prefix)) {
result[transformKey(prefix)] = payload;
return result;
}
if (safe && Array.isArray(payload)) {
result[transformKey(prefix || 'array')] = payload;
return result;
}
if (depth !== undefined && step >= depth) {
result[transformKey(prefix || 'root')] = payload;
return result;
}
if (Array.isArray(payload)) {
payload.forEach((item, index) => {
const arrayKey = prefix ? `${prefix}${delimiter}${index}` : `${index}`;
if (typeof item === 'object' && item !== null) {
Object.assign(result, Flattener._flatten(item, config, arrayKey, step + 1));
}
else {
result[transformKey(arrayKey)] = item;
}
});
return result;
}
if (typeof payload !== 'object' || payload === null) {
if (prefix)
result[transformKey(prefix)] = payload;
return result;
}
for (const key in payload) {
if (!Object.prototype.hasOwnProperty.call(payload, key))
continue;
const value = payload[key];
const fullKey = prefix ? `${prefix}${delimiter}${key}` : key;
if (typeof value === 'object' && value !== null) {
Object.assign(result, Flattener._flatten(value, config, fullKey, step + 1));
}
else {
result[transformKey(fullKey)] = value;
}
}
return result;
}
static _unflatten(flat, config) {
const result = {};
const { delimiter, overwrite, object, transformKey } = config;
// Helper function to safely get numeric key
const getKey = (key) => {
const parsedKey = Number(key);
return isNaN(parsedKey) || key.indexOf('.') !== -1 || object
? key
: parsedKey;
};
// Helper function to check if value is an object
const isObject = (value) => {
const type = Object.prototype.toString.call(value);
return type === '[object Object]' || type === '[object Array]';
};
for (const flatKey in flat) {
const value = flat[flatKey];
const keys = flatKey.split(delimiter).map(transformKey);
let current = result;
for (let i = 0; i < keys.length; i++) {
const key = getKey(keys[i]);
const isLast = i === keys.length - 1;
if (isLast) {
// Final key - assign the value
if (!overwrite &&
current[key] !== undefined &&
!isObject(current[key])) {
// Don't overwrite existing non-object values unless overwrite is true
continue;
}
current[key] = value;
}
else {
// Intermediate key - ensure path exists
const nextKey = keys[i + 1];
const nextIsNumeric = !isNaN(Number(nextKey)) && !object;
if (current[key] === undefined || current[key] === null) {
// Create new container
current[key] = nextIsNumeric ? [] : {};
}
else if (!isObject(current[key])) {
// Existing non-object value
if (overwrite) {
current[key] = nextIsNumeric ? [] : {};
}
else {
// Can't continue down this path without overwriting
break;
}
}
// If it's already an object/array, continue using it
current = current[key];
}
}
}
return result;
}
// Optional utility methods
static flatten(payload, config) {
return new Flattener(payload).flatten(config);
}
static unflatten(flat, config) {
return new Flattener(flat).unflatten(config);
}
}
exports.Flattener = Flattener;
exports.default = Flattener;
//# sourceMappingURL=index.js.map