chore(pricing): Pricing retrieval improvements (#12128)
**What** I have removed the check for the context key where it was fetching all attributes available and then stripping out the one that does not exists.. On big dataset these would remove multiple hundreds of ms of query execution
This commit is contained in:
committed by
GitHub
parent
6ae1e7b708
commit
07252691c5
+219
-1
@@ -18,7 +18,14 @@ describe("flattenObjectToKeyValuePairs", function () {
|
||||
},
|
||||
{
|
||||
product_id: "product-2",
|
||||
product: { id: "product-2" },
|
||||
product: {
|
||||
id: "product-2",
|
||||
something: [
|
||||
{
|
||||
id: "test",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -31,6 +38,217 @@ describe("flattenObjectToKeyValuePairs", function () {
|
||||
"customer.groups.name": ["test", "test 2"],
|
||||
"items.product_id": ["product-1", "product-2"],
|
||||
"items.product.id": ["product-1", "product-2"],
|
||||
"items.product.something.id": ["test"],
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle complex nested objects", function () {
|
||||
const cart = {
|
||||
id: "cart_01JRDH08QD8CZ0KJDVE410KM1J",
|
||||
currency_code: "usd",
|
||||
email: "tony@stark-industries.com",
|
||||
region_id: "reg_01JRDH08ENY3276P6133BVXGWJ",
|
||||
created_at: "2025-04-09T14:59:24.526Z",
|
||||
updated_at: "2025-04-09T14:59:24.526Z",
|
||||
completed_at: null,
|
||||
total: 1500,
|
||||
subtotal: 1428.5714285714287,
|
||||
tax_total: 71.42857142857143,
|
||||
discount_total: 0,
|
||||
discount_subtotal: 0,
|
||||
discount_tax_total: 0,
|
||||
original_total: 1500,
|
||||
original_tax_total: 71.42857142857143,
|
||||
item_total: 1500,
|
||||
item_subtotal: 1428.5714285714287,
|
||||
item_tax_total: 71.42857142857143,
|
||||
original_item_total: 1500,
|
||||
original_item_subtotal: 1428.5714285714287,
|
||||
original_item_tax_total: 71.42857142857143,
|
||||
shipping_total: 0,
|
||||
shipping_subtotal: 0,
|
||||
shipping_tax_total: 0,
|
||||
original_shipping_tax_total: 0,
|
||||
original_shipping_subtotal: 0,
|
||||
original_shipping_total: 0,
|
||||
credit_line_subtotal: 0,
|
||||
credit_line_tax_total: 0,
|
||||
credit_line_total: 0,
|
||||
metadata: null,
|
||||
sales_channel_id: "sc_01JRDH08KWX1AR5SB0A3THWWQQ",
|
||||
shipping_address_id: "caaddr_01JRDH08QDXHV9SJXKHT04TXK0",
|
||||
customer_id: "cus_01JRDH08ATYB5AMFEZDTWCQWNK",
|
||||
items: [
|
||||
{
|
||||
id: "cali_01JRDH08QDQH3CB1DE4S79HREC",
|
||||
thumbnail: null,
|
||||
variant_id: "variant_01JRDH08GJCZQB4GZCDDTYMD1V",
|
||||
product_id: "prod_01JRDH08FPZ6QBZQ096B310RM7",
|
||||
product_type_id: null,
|
||||
product_title: "Medusa T-Shirt",
|
||||
product_description: null,
|
||||
product_subtitle: null,
|
||||
product_type: null,
|
||||
product_collection: null,
|
||||
product_handle: "t-shirt",
|
||||
variant_sku: "SHIRT-S-BLACK",
|
||||
variant_barcode: null,
|
||||
variant_title: "S / Black",
|
||||
requires_shipping: true,
|
||||
metadata: {},
|
||||
created_at: "2025-04-09T14:59:24.526Z",
|
||||
updated_at: "2025-04-09T14:59:24.526Z",
|
||||
title: "S / Black",
|
||||
quantity: 1,
|
||||
unit_price: 1500,
|
||||
compare_at_unit_price: null,
|
||||
is_tax_inclusive: true,
|
||||
tax_lines: [
|
||||
{
|
||||
id: "calitxl_01JRDH08RJEQ4WXXDTJYWV7B4M",
|
||||
description: "CA Default Rate",
|
||||
code: "CADEFAULT",
|
||||
rate: 5,
|
||||
provider_id: "system",
|
||||
},
|
||||
],
|
||||
adjustments: [],
|
||||
product: {
|
||||
id: "prod_01JRDH08FPZ6QBZQ096B310RM7",
|
||||
collection_id: null,
|
||||
type_id: null,
|
||||
categories: [],
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
shipping_methods: [],
|
||||
shipping_address: {
|
||||
id: "caaddr_01JRDH08QDXHV9SJXKHT04TXK0",
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
company: null,
|
||||
address_1: "test address 1",
|
||||
address_2: "test address 2",
|
||||
city: "SF",
|
||||
postal_code: "94016",
|
||||
country_code: "US",
|
||||
province: "CA",
|
||||
phone: null,
|
||||
},
|
||||
billing_address: null,
|
||||
credit_lines: [],
|
||||
customer: {
|
||||
id: "cus_01JRDH08ATYB5AMFEZDTWCQWNK",
|
||||
email: "tony@stark-industries.com",
|
||||
groups: [],
|
||||
},
|
||||
region: {
|
||||
id: "reg_01JRDH08ENY3276P6133BVXGWJ",
|
||||
name: "US",
|
||||
currency_code: "usd",
|
||||
automatic_taxes: true,
|
||||
countries: [
|
||||
{
|
||||
iso_2: "us",
|
||||
iso_3: "usa",
|
||||
num_code: "840",
|
||||
name: "UNITED STATES",
|
||||
display_name: "United States",
|
||||
region_id: "reg_01JRDH08ENY3276P6133BVXGWJ",
|
||||
metadata: null,
|
||||
created_at: "2025-04-09T14:59:20.275Z",
|
||||
updated_at: "2025-04-09T14:59:24.250Z",
|
||||
deleted_at: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
promotions: [],
|
||||
}
|
||||
|
||||
const keyValueParis = flattenObjectToKeyValuePairs(cart)
|
||||
console.log(JSON.stringify(keyValueParis, null, 2))
|
||||
expect(keyValueParis).toEqual({
|
||||
id: "cart_01JRDH08QD8CZ0KJDVE410KM1J",
|
||||
currency_code: "usd",
|
||||
email: "tony@stark-industries.com",
|
||||
region_id: "reg_01JRDH08ENY3276P6133BVXGWJ",
|
||||
created_at: "2025-04-09T14:59:24.526Z",
|
||||
updated_at: "2025-04-09T14:59:24.526Z",
|
||||
total: 1500,
|
||||
subtotal: 1428.5714285714287,
|
||||
tax_total: 71.42857142857143,
|
||||
discount_total: 0,
|
||||
discount_subtotal: 0,
|
||||
discount_tax_total: 0,
|
||||
original_total: 1500,
|
||||
original_tax_total: 71.42857142857143,
|
||||
item_total: 1500,
|
||||
"items.adjustments": [],
|
||||
item_subtotal: 1428.5714285714287,
|
||||
item_tax_total: 71.42857142857143,
|
||||
original_item_total: 1500,
|
||||
original_item_subtotal: 1428.5714285714287,
|
||||
original_item_tax_total: 71.42857142857143,
|
||||
shipping_total: 0,
|
||||
shipping_subtotal: 0,
|
||||
shipping_tax_total: 0,
|
||||
original_shipping_tax_total: 0,
|
||||
original_shipping_subtotal: 0,
|
||||
original_shipping_total: 0,
|
||||
credit_line_subtotal: 0,
|
||||
credit_line_tax_total: 0,
|
||||
credit_line_total: 0,
|
||||
sales_channel_id: "sc_01JRDH08KWX1AR5SB0A3THWWQQ",
|
||||
shipping_address_id: "caaddr_01JRDH08QDXHV9SJXKHT04TXK0",
|
||||
customer_id: "cus_01JRDH08ATYB5AMFEZDTWCQWNK",
|
||||
"items.id": ["cali_01JRDH08QDQH3CB1DE4S79HREC"],
|
||||
"items.variant_id": ["variant_01JRDH08GJCZQB4GZCDDTYMD1V"],
|
||||
"items.product_id": ["prod_01JRDH08FPZ6QBZQ096B310RM7"],
|
||||
"items.product_title": ["Medusa T-Shirt"],
|
||||
"items.product_handle": ["t-shirt"],
|
||||
"items.variant_sku": ["SHIRT-S-BLACK"],
|
||||
"items.variant_title": ["S / Black"],
|
||||
"items.requires_shipping": [true],
|
||||
"items.created_at": ["2025-04-09T14:59:24.526Z"],
|
||||
"items.updated_at": ["2025-04-09T14:59:24.526Z"],
|
||||
"items.title": ["S / Black"],
|
||||
"items.quantity": [1],
|
||||
"items.unit_price": [1500],
|
||||
"items.is_tax_inclusive": [true],
|
||||
"items.tax_lines.id": ["calitxl_01JRDH08RJEQ4WXXDTJYWV7B4M"],
|
||||
"items.tax_lines.description": ["CA Default Rate"],
|
||||
"items.tax_lines.code": ["CADEFAULT"],
|
||||
"items.tax_lines.rate": [5],
|
||||
"items.tax_lines.provider_id": ["system"],
|
||||
"items.product.id": ["prod_01JRDH08FPZ6QBZQ096B310RM7"],
|
||||
"items.product.categories": [],
|
||||
"items.product.tags": [],
|
||||
shipping_methods: [],
|
||||
"shipping_address.id": "caaddr_01JRDH08QDXHV9SJXKHT04TXK0",
|
||||
"shipping_address.address_1": "test address 1",
|
||||
"shipping_address.address_2": "test address 2",
|
||||
"shipping_address.city": "SF",
|
||||
"shipping_address.postal_code": "94016",
|
||||
"shipping_address.country_code": "US",
|
||||
"shipping_address.province": "CA",
|
||||
credit_lines: [],
|
||||
"customer.id": "cus_01JRDH08ATYB5AMFEZDTWCQWNK",
|
||||
"customer.email": "tony@stark-industries.com",
|
||||
"customer.groups": [],
|
||||
"region.id": "reg_01JRDH08ENY3276P6133BVXGWJ",
|
||||
"region.name": "US",
|
||||
"region.currency_code": "usd",
|
||||
"region.automatic_taxes": true,
|
||||
"region.countries.iso_2": ["us"],
|
||||
"region.countries.iso_3": ["usa"],
|
||||
"region.countries.num_code": ["840"],
|
||||
"region.countries.name": ["UNITED STATES"],
|
||||
"region.countries.display_name": ["United States"],
|
||||
"region.countries.region_id": ["reg_01JRDH08ENY3276P6133BVXGWJ"],
|
||||
"region.countries.created_at": ["2025-04-09T14:59:20.275Z"],
|
||||
"region.countries.updated_at": ["2025-04-09T14:59:24.250Z"],
|
||||
promotions: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,116 +27,284 @@ type NestedObject = {
|
||||
"product.categories.id": ["test-category"],
|
||||
"product.categories.name": ["Test Category"]
|
||||
}
|
||||
|
||||
Null and undefined values are excluded from the result.
|
||||
*/
|
||||
export function flattenObjectToKeyValuePairs(obj: NestedObject): NestedObject {
|
||||
const result: NestedObject = {}
|
||||
|
||||
// Find all paths that contain arrays of objects
|
||||
function findArrayPaths(
|
||||
obj: unknown,
|
||||
currentPath: string[] = []
|
||||
): string[][] {
|
||||
const paths: string[][] = []
|
||||
|
||||
if (!obj || typeof obj !== "object") {
|
||||
return paths
|
||||
function shouldPreserveArray(value: any[], path: string[]): boolean {
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If it's an array of objects, add this path
|
||||
if (Array.isArray(obj) && obj.length > 0 && isObject(obj[0])) {
|
||||
paths.push(currentPath)
|
||||
if (value.some((item) => isObject(item) && !Array.isArray(item))) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check all properties
|
||||
if (isObject(obj)) {
|
||||
Object.entries(obj as Record<string, unknown>).forEach(([key, value]) => {
|
||||
const newPath = [...currentPath, key]
|
||||
paths.push(...findArrayPaths(value, newPath))
|
||||
})
|
||||
if (value.some((item) => Array.isArray(item))) {
|
||||
return true
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
if (path.length > 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Extract array values at a specific path
|
||||
function getArrayValues(obj: unknown, path: string[]): unknown[] {
|
||||
const arrayObj = path.reduce((acc: unknown, key: string) => {
|
||||
if (acc && isObject(acc)) {
|
||||
return (acc as Record<string, unknown>)[key]
|
||||
if (value.length > 1) {
|
||||
const firstType = typeof value[0]
|
||||
const allSameType = value.every(
|
||||
(item) =>
|
||||
typeof item === firstType && !isObject(item) && !Array.isArray(item)
|
||||
)
|
||||
if (allSameType) {
|
||||
return true
|
||||
}
|
||||
return undefined
|
||||
}, obj)
|
||||
}
|
||||
|
||||
if (!Array.isArray(arrayObj)) return []
|
||||
|
||||
return arrayObj
|
||||
return false
|
||||
}
|
||||
|
||||
// Process non-array paths
|
||||
function processRegularPaths(obj: unknown, prefix = ""): void {
|
||||
if (!obj || typeof obj !== "object") {
|
||||
result[prefix] = obj
|
||||
/**
|
||||
* Normalize array values - unwrap single-item arrays and handle empty arrays
|
||||
*/
|
||||
function normalizeArrayValue(value: any, path: string[]): any {
|
||||
if (!Array.isArray(value)) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (
|
||||
value.length === 1 &&
|
||||
Array.isArray(value[0]) &&
|
||||
value[0].length === 0
|
||||
) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (shouldPreserveArray(value, path)) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (value.length === 1 && !isObject(value[0]) && !Array.isArray(value[0])) {
|
||||
return value[0]
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively process an object/array and flatten it
|
||||
*/
|
||||
function processPath(value: any, currentPath: string[] = []): void {
|
||||
// Handle null, undefined, or primitive values
|
||||
if (!value || typeof value !== "object") {
|
||||
if (value !== null && value !== undefined && currentPath.length > 0) {
|
||||
result[currentPath.join(".")] = value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) return
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 0) {
|
||||
if (currentPath.length > 0) {
|
||||
result[currentPath.join(".")] = []
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Object.entries(obj as Record<string, unknown>).forEach(([key, value]) => {
|
||||
const newPrefix = prefix ? `${prefix}.${key}` : key
|
||||
if (value && isObject(value) && !Array.isArray(value)) {
|
||||
processRegularPaths(value, newPrefix)
|
||||
} else if (!Array.isArray(value)) {
|
||||
result[newPrefix] = value
|
||||
if (value.some((item) => isObject(item) && !Array.isArray(item))) {
|
||||
extractPropertiesFromArray(value, currentPath)
|
||||
} else if (value.some((item) => Array.isArray(item))) {
|
||||
const allValues: any[] = []
|
||||
const flattenedObjects: Record<string, any>[] = []
|
||||
|
||||
const flattenArray = (arr: any[], collector: any[] = []): void => {
|
||||
arr.forEach((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
flattenArray(item, collector)
|
||||
} else if (isObject(item)) {
|
||||
collector.push(item)
|
||||
} else if (item !== null && item !== undefined) {
|
||||
allValues.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
flattenArray(value, flattenedObjects)
|
||||
|
||||
if (flattenedObjects.length > 0) {
|
||||
extractPropertiesFromArray(flattenedObjects, currentPath)
|
||||
} else if (allValues.length > 0) {
|
||||
result[currentPath.join(".")] = normalizeArrayValue(
|
||||
allValues,
|
||||
currentPath
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const cleanedValues = value.filter((v) => v !== null && v !== undefined)
|
||||
if (cleanedValues.length > 0) {
|
||||
result[currentPath.join(".")] = normalizeArrayValue(
|
||||
cleanedValues,
|
||||
currentPath
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Object.entries(value).forEach(([key, propValue]) => {
|
||||
const newPath = [...currentPath, key]
|
||||
|
||||
if (propValue === null || propValue === undefined) {
|
||||
return
|
||||
} else if (Array.isArray(propValue)) {
|
||||
if (propValue.length === 0) {
|
||||
result[newPath.join(".")] = []
|
||||
} else {
|
||||
processPath(propValue, newPath)
|
||||
}
|
||||
} else if (isObject(propValue)) {
|
||||
processPath(propValue, newPath)
|
||||
} else {
|
||||
result[newPath.join(".")] = propValue
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Process the object
|
||||
processRegularPaths(obj)
|
||||
/**
|
||||
* Extract all properties from an array of objects and store them
|
||||
*/
|
||||
function extractPropertiesFromArray(
|
||||
array: any[],
|
||||
basePath: string[] = []
|
||||
): void {
|
||||
if (!array.length) return
|
||||
|
||||
// Find and process array paths
|
||||
const arrayPaths = findArrayPaths(obj)
|
||||
arrayPaths.forEach((path) => {
|
||||
const pathStr = path.join(".")
|
||||
const arrayObjects = getArrayValues(obj, path)
|
||||
// Collect all unique keys from all objects in the array
|
||||
const allKeys = new Set<string>()
|
||||
array.forEach((item) => {
|
||||
if (isObject(item) && !Array.isArray(item)) {
|
||||
Object.keys(item).forEach((key) => allKeys.add(key))
|
||||
}
|
||||
})
|
||||
|
||||
if (Array.isArray(arrayObjects) && arrayObjects.length > 0) {
|
||||
// Get all possible keys from the array objects
|
||||
const keys = new Set<string>()
|
||||
arrayObjects.forEach((item) => {
|
||||
if (item && isObject(item)) {
|
||||
Object.keys(item as object).forEach((k) => keys.add(k))
|
||||
}
|
||||
})
|
||||
allKeys.forEach((key) => {
|
||||
const valuePath = [...basePath, key]
|
||||
const values: any[] = []
|
||||
|
||||
// Process each key
|
||||
keys.forEach((key) => {
|
||||
const values = arrayObjects
|
||||
.map((item) => {
|
||||
if (item && isObject(item)) {
|
||||
return (item as Record<string, unknown>)[key]
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
.filter((v) => v !== undefined)
|
||||
|
||||
if (values.length > 0) {
|
||||
const newPath = `${pathStr}.${key}`
|
||||
if (values.every((v) => isObject(v) && !Array.isArray(v))) {
|
||||
// If these are all objects, recursively process them
|
||||
const subObj = { [key]: values }
|
||||
const subResult = flattenObjectToKeyValuePairs(subObj)
|
||||
Object.entries(subResult).forEach(([k, v]) => {
|
||||
const finalPath = `${pathStr}.${k}`
|
||||
result[finalPath] = v
|
||||
})
|
||||
} else {
|
||||
result[newPath] = values
|
||||
array.forEach((item) => {
|
||||
if (isObject(item) && !Array.isArray(item) && key in item) {
|
||||
const itemValue = item[key]
|
||||
if (itemValue !== null && itemValue !== undefined) {
|
||||
values.push(itemValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (values.length === 0) return
|
||||
|
||||
if (values.every((v) => isObject(v) && !Array.isArray(v))) {
|
||||
extractNestedObjectProperties(values, valuePath)
|
||||
} else if (values.some((v) => Array.isArray(v))) {
|
||||
if (values.every((v) => Array.isArray(v) && v.length === 0)) {
|
||||
result[valuePath.join(".")] = []
|
||||
} else {
|
||||
const flattenedArray: any[] = []
|
||||
for (const arrayValue of values) {
|
||||
if (Array.isArray(arrayValue)) {
|
||||
if (arrayValue.some((v) => isObject(v) && !Array.isArray(v))) {
|
||||
extractPropertiesFromArray(arrayValue, valuePath)
|
||||
} else {
|
||||
flattenedArray.push(...arrayValue)
|
||||
}
|
||||
} else {
|
||||
flattenedArray.push(arrayValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
flattenedArray.length > 0 &&
|
||||
!flattenedArray.some((v) => isObject(v) && !Array.isArray(v))
|
||||
) {
|
||||
result[valuePath.join(".")] = normalizeArrayValue(
|
||||
flattenedArray,
|
||||
valuePath
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[valuePath.join(".")] = normalizeArrayValue(values, valuePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract properties from nested objects and add them to the result
|
||||
*/
|
||||
function extractNestedObjectProperties(
|
||||
objects: any[],
|
||||
basePath: string[] = []
|
||||
): void {
|
||||
if (!objects.length) return
|
||||
|
||||
// Collect all unique keys from all objects
|
||||
const allNestedKeys = new Set<string>()
|
||||
objects.forEach((obj) => {
|
||||
if (isObject(obj) && !Array.isArray(obj)) {
|
||||
Object.keys(obj).forEach((key) => allNestedKeys.add(key))
|
||||
}
|
||||
})
|
||||
|
||||
allNestedKeys.forEach((nestedKey) => {
|
||||
const nestedPath = [...basePath, nestedKey]
|
||||
const nestedValues: any[] = []
|
||||
|
||||
objects.forEach((obj) => {
|
||||
if (isObject(obj) && !Array.isArray(obj) && nestedKey in obj) {
|
||||
const nestedValue = obj[nestedKey]
|
||||
if (nestedValue !== null && nestedValue !== undefined) {
|
||||
nestedValues.push(nestedValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (nestedValues.length === 0) return
|
||||
|
||||
if (nestedValues.every((v) => isObject(v) && !Array.isArray(v))) {
|
||||
extractNestedObjectProperties(nestedValues, nestedPath)
|
||||
} else if (nestedValues.some((v) => Array.isArray(v))) {
|
||||
if (nestedValues.every((v) => Array.isArray(v) && v.length === 0)) {
|
||||
result[nestedPath.join(".")] = []
|
||||
} else {
|
||||
const allArrayItems: any[] = []
|
||||
for (const arrayValue of nestedValues) {
|
||||
if (Array.isArray(arrayValue)) {
|
||||
allArrayItems.push(...arrayValue)
|
||||
} else {
|
||||
allArrayItems.push(arrayValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
allArrayItems.some((item) => isObject(item) && !Array.isArray(item))
|
||||
) {
|
||||
extractPropertiesFromArray(allArrayItems, nestedPath)
|
||||
} else {
|
||||
result[nestedPath.join(".")] = normalizeArrayValue(
|
||||
allArrayItems,
|
||||
nestedPath
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result[nestedPath.join(".")] = normalizeArrayValue(
|
||||
nestedValues,
|
||||
nestedPath
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
processPath(obj)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user