Extract functions

This commit is contained in:
Ola Hungerford
2025-02-26 19:55:01 -07:00
parent 0e29e2c1cf
commit 0b105b29c1
3 changed files with 227 additions and 131 deletions

View File

@@ -0,0 +1,152 @@
import { JsonValue } from "../components/DynamicJsonForm";
export type JsonObject = { [key: string]: JsonValue };
/**
* Updates a value at a specific path in a nested JSON structure
* @param obj The original JSON value
* @param path Array of keys/indices representing the path to the value
* @param value The new value to set
* @returns A new JSON value with the updated path
*/
export function updateValueAtPath(
obj: JsonValue,
path: string[],
value: JsonValue
): JsonValue {
if (path.length === 0) return value;
// Initialize if null/undefined
if (obj === null || obj === undefined) {
obj = !isNaN(Number(path[0])) ? [] : {};
}
// Handle arrays
if (Array.isArray(obj)) {
return updateArray(obj, path, value);
}
// Handle objects
else if (typeof obj === "object" && obj !== null) {
return updateObject(obj as JsonObject, path, value);
}
// Cannot update primitives
else {
console.error(
`Cannot update path ${path.join(".")} in non-object/array value:`,
obj
);
return obj;
}
}
/**
* Updates an array at a specific path
*/
function updateArray(
array: JsonValue[],
path: string[],
value: JsonValue
): JsonValue[] {
const [index, ...restPath] = path;
const arrayIndex = Number(index);
// Validate array index
if (isNaN(arrayIndex)) {
console.error(`Invalid array index: ${index}`);
return array;
}
// Check array bounds
if (arrayIndex < 0) {
console.error(`Array index out of bounds: ${arrayIndex} < 0`);
return array;
}
const newArray = [...array];
if (restPath.length === 0) {
newArray[arrayIndex] = value;
} else {
// Ensure index position exists
if (arrayIndex >= array.length) {
console.warn(`Extending array to index ${arrayIndex}`);
newArray.length = arrayIndex + 1;
newArray.fill(null, array.length, arrayIndex);
}
newArray[arrayIndex] = updateValueAtPath(
newArray[arrayIndex],
restPath,
value
);
}
return newArray;
}
/**
* Updates an object at a specific path
*/
function updateObject(
obj: JsonObject,
path: string[],
value: JsonValue
): JsonObject {
const [key, ...restPath] = path;
// Validate object key
if (typeof key !== "string") {
console.error(`Invalid object key: ${key}`);
return obj;
}
const newObj = { ...obj };
if (restPath.length === 0) {
newObj[key] = value;
} else {
// Ensure key exists
if (!(key in newObj)) {
console.warn(`Creating new key in object: ${key}`);
newObj[key] = {};
}
newObj[key] = updateValueAtPath(newObj[key], restPath, value);
}
return newObj;
}
/**
* Gets a value at a specific path in a nested JSON structure
* @param obj The JSON value to traverse
* @param path Array of keys/indices representing the path to the value
* @param defaultValue Value to return if path doesn't exist
* @returns The value at the path, or defaultValue if not found
*/
export function getValueAtPath(
obj: JsonValue,
path: string[],
defaultValue: JsonValue = null
): JsonValue {
if (path.length === 0) return obj;
const [first, ...rest] = path;
if (obj === null || obj === undefined) {
return defaultValue;
}
if (Array.isArray(obj)) {
const index = Number(first);
if (isNaN(index) || index < 0 || index >= obj.length) {
return defaultValue;
}
return getValueAtPath(obj[index], rest, defaultValue);
}
if (typeof obj === "object" && obj !== null) {
if (!(first in obj)) {
return defaultValue;
}
return getValueAtPath((obj as JsonObject)[first], rest, defaultValue);
}
return defaultValue;
}

View File

@@ -0,0 +1,72 @@
import { JsonValue, JsonSchemaType } from "../components/DynamicJsonForm";
import { JsonObject } from "./jsonPathUtils";
/**
* Generates a default value based on a JSON schema type
* @param schema The JSON schema definition
* @returns A default value matching the schema type
*/
export function generateDefaultValue(schema: JsonSchemaType): JsonValue {
switch (schema.type) {
case "string":
return "";
case "number":
case "integer":
return 0;
case "boolean":
return false;
case "array":
return [];
case "object": {
const obj: JsonObject = {};
if (schema.properties) {
Object.entries(schema.properties).forEach(([key, prop]) => {
obj[key] = generateDefaultValue(prop);
});
}
return obj;
}
default:
return null;
}
}
/**
* Formats a field key into a human-readable label
* @param key The field key to format
* @returns A formatted label string
*/
export function formatFieldLabel(key: string): string {
return key
.replace(/([A-Z])/g, " $1") // Insert space before capital letters
.replace(/_/g, " ") // Replace underscores with spaces
.replace(/^\w/, (c) => c.toUpperCase()); // Capitalize first letter
}
/**
* Validates if a value conforms to a JSON schema
* @param value The value to validate
* @param schema The JSON schema to validate against
* @returns True if valid, false otherwise
*/
export function validateValueAgainstSchema(
value: JsonValue,
schema: JsonSchemaType
): boolean {
// Basic type validation
switch (schema.type) {
case "string":
return typeof value === "string";
case "number":
case "integer":
return typeof value === "number";
case "boolean":
return typeof value === "boolean";
case "array":
return Array.isArray(value);
case "object":
return typeof value === "object" && value !== null && !Array.isArray(value);
default:
return true;
}
}