Repository: microsoft/data-formulator
File: src/app/utils.tsx (TypeScript)

Code before:
                // if the template hold is a string, then we only instantiate a string value
                // if the template hold is a dict, then we embed the actual embeding object
                if (typeof ref[path[path.length - 1]] === 'string' || ref[path[path.length - 1]] instanceof String) {
                    ref[path[path.length - 1]] = encodingObj['field'];
                } else {
                    let prebuiltEntries = ref[path[path.length - 1]] != undefined ? Object.entries(ref[path[path.length - 1]]) : []
                    ref[path[path.length - 1]] = Object.fromEntries([...prebuiltEntries, ...Object.entries(encodingObj)]);
                }
                
            }
        }
    }

    if (vgObj.encoding?.column != undefined && vgObj.encoding?.row == undefined) {
        vgObj['encoding']['facet'] = vgObj['encoding']['column'];
        vgObj['encoding']['facet']['columns'] = 6;
        delete vgObj['encoding']['column'];
    }

    // use post processor to handle smart chart instantiation
    if (chartTemplate.postProcessor) {
        vgObj = chartTemplate.postProcessor(vgObj, workingTable);
    }

    // this is the data that will be assembled into the vega chart
    let values = JSON.parse(JSON.stringify(workingTable));
    values = values.map((r: any) => { 
        let keys = Object.keys(r);
        let temporalKeys = keys.filter((k: string) => conceptShelfItems.some(concept => concept.name == k && (concept.type == "date" || concept.semanticType == "Year")));
        for (let temporalKey of temporalKeys) {
            r[temporalKey] = String(r[temporalKey]);
        }
        return r;
    })

    // Handle nominal axes with many entries
    for (const channel of ['x', 'y', 'column', 'row', 'xOffset']) {
        const encoding = vgObj.encoding?.[channel];
        if (encoding?.type === 'nominal') {
            const fieldName = encoding.field;
            const uniqueValues = [...new Set(values.map((r: any) => r[fieldName]))];
            
            let valuesToKeep: any[];
            if (uniqueValues.length > maxNominalValues) {

                if (channel == 'x' || channel == 'y') {
                    const oppositeChannel = channel === 'x' ? 'y' : 'x';
                    const oppositeEncoding = vgObj.encoding?.[oppositeChannel];
                    
                    if (oppositeEncoding?.type === 'quantitative') {
                        // Sort by the quantitative field and take top maxNominalValues

Review comment (suggestion):
Try [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone) instead of JSON.parse(JSON.stringify(x))
https://dev.to/dharamgfx/bye-bye-jsonstringify-and-obj-hello-structuredclone-2e4c

Code after:
                // if the template hold is a string, then we only instantiate a string value
                // if the template hold is a dict, then we embed the actual embeding object
                if (typeof ref[path[path.length - 1]] === 'string' || ref[path[path.length - 1]] instanceof String) {
                    ref[path[path.length - 1]] = encodingObj['field'];
                } else {
                    let prebuiltEntries = ref[path[path.length - 1]] != undefined ? Object.entries(ref[path[path.length - 1]]) : []
                    ref[path[path.length - 1]] = Object.fromEntries([...prebuiltEntries, ...Object.entries(encodingObj)]);
                }
                
            }
        }
    }

    if (vgObj.encoding?.column != undefined && vgObj.encoding?.row == undefined) {
        vgObj['encoding']['facet'] = vgObj['encoding']['column'];
        vgObj['encoding']['facet']['columns'] = 6;
        delete vgObj['encoding']['column'];
    }

    // use post processor to handle smart chart instantiation
    if (chartTemplate.postProcessor) {
        vgObj = chartTemplate.postProcessor(vgObj, workingTable);
    }

    // this is the data that will be assembled into the vega chart
    let values = structuredClone(workingTable);
    values = values.map((r: any) => { 
        let keys = Object.keys(r);
        let temporalKeys = keys.filter((k: string) => conceptShelfItems.some(concept => concept.name == k && (concept.type == "date" || concept.semanticType == "Year")));
        for (let temporalKey of temporalKeys) {
            r[temporalKey] = String(r[temporalKey]);
        }
        return r;
    })

    // Handle nominal axes with many entries
    for (const channel of ['x', 'y', 'column', 'row', 'xOffset']) {
        const encoding = vgObj.encoding?.[channel];
        if (encoding?.type === 'nominal') {
            const fieldName = encoding.field;
            const uniqueValues = [...new Set(values.map((r: any) => r[fieldName]))];
            
            let valuesToKeep: any[];
            if (uniqueValues.length > maxNominalValues) {

                if (channel == 'x' || channel == 'y') {
                    const oppositeChannel = channel === 'x' ? 'y' : 'x';
                    const oppositeEncoding = vgObj.encoding?.[oppositeChannel];
                    
                    if (oppositeEncoding?.type === 'quantitative') {
                        // Sort by the quantitative field and take top maxNominalValues