Snippets
Flattened Object to Merged
Utility functions to convert flattened dot-notation objects to nested objects
TS
/**
* Creates a key in a dot-notation object from the
* given separator and a list of arguments.
*/
function dotter(sep: string) {
return function dot(...args: string[]) {
return args.join(sep);
};
}
/**
* Transforms a dot-notation object to an nested object
* with the given dot character.
*/
function merger(sep: string) {
return function merge(obj: Record<string, unknown>) {
const result: Record<string, any> = {};
Object.keys(obj).forEach((key) => {
const path = key.split(sep);
path.reduce((acc, curr, idx) => {
const isEnd = idx === path.length - 1;
acc[curr] = isEnd ? obj[key] : acc[curr] || {};
return acc[curr];
}, result);
});
return result;
};
}
/**
* Makes a dot function and a merge function given the dot character.
*/
export function createDotter(sep = ".") {
return {
dot: dotter(sep),
merge: merger(sep),
};
}
/**
* Creates a key in a dot-notation object from the
* given separator and a list of arguments.
*/
function dotter(sep: string) {
return function dot(...args: string[]) {
return args.join(sep);
};
}
/**
* Transforms a dot-notation object to an nested object
* with the given dot character.
*/
function merger(sep: string) {
return function merge(obj: Record<string, unknown>) {
const result: Record<string, any> = {};
Object.keys(obj).forEach((key) => {
const path = key.split(sep);
path.reduce((acc, curr, idx) => {
const isEnd = idx === path.length - 1;
acc[curr] = isEnd ? obj[key] : acc[curr] || {};
return acc[curr];
}, result);
});
return result;
};
}
/**
* Makes a dot function and a merge function given the dot character.
*/
export function createDotter(sep = ".") {
return {
dot: dotter(sep),
merge: merger(sep),
};
}
Examples
LazyForm.tsx
A lazy form component that extracts and merges form data directly from the DOM. See the live demo in the next section 👇
TSX
"use client";
import { CodeHighlighter } from "components/CodeHighlighter";
import { useRef, useState } from "react";
import { createDotter } from "./dot-to-merge";
import styles from "./styles.module.css";
import { useIds } from "./useIds";
const { dot, merge } = createDotter(".");
export const LazyForm = () => {
const {
ids: todoIds,
addId: addTodoId,
removeId: removeTodoId,
resetIds: resetTodoIds,
} = useIds();
const [result, setResult] = useState<any>({});
const formRef = useRef<HTMLFormElement>(null);
const updateResult = () => {
if (!formRef.current) return;
const formData = new FormData(formRef.current);
const flatObject = Object.fromEntries(formData.entries());
const data = merge(flatObject);
setResult(data);
};
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
updateResult();
};
const onReset = () => {
setResult({});
resetTodoIds();
};
return (
<div className={styles.top}>
<p>
This form uses dotted names such as: <code>user.username</code>,{" "}
<code>user.email</code>, etc. Fill the form then click Submit or press
Enter to see the parsed and merged form data in "Result".
<br />
</p>
<div className={styles.container}>
<form
ref={formRef}
className={styles.form}
onSubmit={onSubmit}
onReset={onReset}
>
<div>
<label>Username</label>
<input type="text" name={dot("user", "username")} />
</div>
<div>
<label>Email</label>
<input type="email" name={dot("user", "email")} />
</div>
<h4>ToDos</h4>
{todoIds.map((id) => {
return (
<div className={styles.row} key={id}>
<label>#{id} Title</label>
<input
type="text"
name={dot("todos", id.toString(), "title")}
/>
<button type="button" onClick={() => removeTodoId(id)}>
x
</button>
</div>
);
})}
<button type="button" onClick={addTodoId}>
Add ToDo
</button>
<div className={styles.actions}>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</form>
<div>
<h4>Merged Form Result</h4>
<CodeHighlighter
language="json"
tag="JSON"
content={JSON.stringify(result, null, 2)}
copy={false}
/>
</div>
</div>
<p>
Tip: This can be used in conjunction with server-side validators (e.g{" "}
<code>zod</code>) to ensure valid form data is supplied to the server.
</p>
</div>
);
};
"use client";
import { CodeHighlighter } from "components/CodeHighlighter";
import { useRef, useState } from "react";
import { createDotter } from "./dot-to-merge";
import styles from "./styles.module.css";
import { useIds } from "./useIds";
const { dot, merge } = createDotter(".");
export const LazyForm = () => {
const {
ids: todoIds,
addId: addTodoId,
removeId: removeTodoId,
resetIds: resetTodoIds,
} = useIds();
const [result, setResult] = useState<any>({});
const formRef = useRef<HTMLFormElement>(null);
const updateResult = () => {
if (!formRef.current) return;
const formData = new FormData(formRef.current);
const flatObject = Object.fromEntries(formData.entries());
const data = merge(flatObject);
setResult(data);
};
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
updateResult();
};
const onReset = () => {
setResult({});
resetTodoIds();
};
return (
<div className={styles.top}>
<p>
This form uses dotted names such as: <code>user.username</code>,{" "}
<code>user.email</code>, etc. Fill the form then click Submit or press
Enter to see the parsed and merged form data in "Result".
<br />
</p>
<div className={styles.container}>
<form
ref={formRef}
className={styles.form}
onSubmit={onSubmit}
onReset={onReset}
>
<div>
<label>Username</label>
<input type="text" name={dot("user", "username")} />
</div>
<div>
<label>Email</label>
<input type="email" name={dot("user", "email")} />
</div>
<h4>ToDos</h4>
{todoIds.map((id) => {
return (
<div className={styles.row} key={id}>
<label>#{id} Title</label>
<input
type="text"
name={dot("todos", id.toString(), "title")}
/>
<button type="button" onClick={() => removeTodoId(id)}>
x
</button>
</div>
);
})}
<button type="button" onClick={addTodoId}>
Add ToDo
</button>
<div className={styles.actions}>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</form>
<div>
<h4>Merged Form Result</h4>
<CodeHighlighter
language="json"
tag="JSON"
content={JSON.stringify(result, null, 2)}
copy={false}
/>
</div>
</div>
<p>
Tip: This can be used in conjunction with server-side validators (e.g{" "}
<code>zod</code>) to ensure valid form data is supplied to the server.
</p>
</div>
);
};
Demo - Lazy Form
This form uses dotted names such as: user.username
, user.email
, etc. Fill the form then click Submit or press Enter to see the parsed and merged form data in "Result".
Merged Form Result
JSON
{}
{}
Tip: This can be used in conjunction with server-side validators (e.g zod
) to ensure valid form data is supplied to the server.