Search code examples
javascriptarraysobjectecmascript-6array-merge

Merge two arrays of objects based on nested object key and value


I have the following two arrays of objects that I want to merge in one array but based on some nested keys and values inside the nested object of the first array.

Here's what I have right now:

const variants = [{
    "name": "Name 1",
    "variationValues": {
        "P00003": "mappedValue1",
        "P00004": "mappedValue4",
    }
}, {
    "name": "Name 2",
    "variationValues": {
        "P00003": "mappedValue2"
    }
}, {
    "name": "Name 3",
    "variationValues": {
        "P00003": "mappedValue3"
    }
}]

const variationAttributes = [{
    "id": "P00003",
    "name": "Variant Name",
    "attributes": [{
        "name": "Variant Name 1",
        "description": "Variant Description 1",
        "attribute": "mappedValue1"
    }, {
        "name": "Variant Name 2",
        "description": "Variant Description 2",
        "attribute": "mappedValue2"
    }, {
        "name": "Variant Name 3",
        "description": "Variant Description 3",
        "attribute": "mappedValue3"
    }, {
        "name": "Variant Name 4",
        "description": "Variant Description 4",
        "attribute": "mappedValue4"
    }]
}, {
    "id": "P00004",
    "name": "Variant Name",
    "attributes": [{
        "name": "Variant Name 4",
        "description": "Variant Description 4",
        "attribute": "mappedValue4"
    }]
}]

And here is the expected result:

const result = [{
    "variants": [{
        "name": "Name 1",
        "variationValues": {
            "P00003": {
                "name": "Variant Name 1",
                "description": "Variant Description 1",
                "attribute": "mappedValue1"
            },
            "P00004": {
                "name": "Variant Name 4",
                "description": "Variant Description 4",
                "attribute": "mappedValue4"
            }
        }
    }, {
        "name": "Name 2",
        "variationValues": {
            "P00003": {
                "name": "Variant Name 2",
                "description": "Variant Description 2",
                "attribute": "mappedValue2"
            }
        }
    }, {
        "name": "Name 3",
        "variationValues": {
            "P00003": {
                "name": "Variant Name 3",
                "description": "Variant Description 3",
                "attribute": "mappedValue3"
            }
        }
    }]
}]

Basically it should come down to the following:

  1. variants.variationValues['P00003'] to map to variationAttributes.id
  2. variants.variationValues[value] to map to variationAttributes.attribute.attribute[value]

I have tried to start using map and reduce methods, but I'm not even sure what's the best place to start.

Any advice will be greatly appreciated, thanks in advance!


Solution

  • The following code combines the two arrays like you specified:

    // Data
    
    const variants = [{"name":"Name 1","variationValues":{"P00003":"mappedValue1"}},{"name":"Name 2","variationValues":{"P00003":"mappedValue2"}},{"name":"Name 3","variationValues":{"P00003":"mappedValue3"}}];
    const variationAttributes = [{"id":"P00003","name":"Variant Name","attributes":[{"name":"Variant Name 1","description":"Variant Description 1","attribute":"mappedValue1"},{"name":"Variant Name 2","description":"Variant Description 2","attribute":"mappedValue2"},{"name":"Variant Name 3","description":"Variant Description 3","attribute":"mappedValue3"}]}];
    
    // Solution code
    
    console.log( getMappedVariants() );
    
    function getMappedVariants() {
        return variants.map((variant) => ({
            ...variant,
            variationValues: mapValuesToAttributes(variant.variationValues),
        }));
    }
    
    function mapValuesToAttributes(values) {
        const attributes = {};
        for (const id in values) attributes[id] = findAttribute(id, values[id]);
        return attributes;
    }
    
    function findAttribute(id, value) {
        const variation = variationAttributes.find((v) => v.id === id);
        const attribute = variation.attributes.find((a) => a.attribute === value);
        return attribute;
    }

    For the sport of it this could be reduced to one single function, but I would argue that this is the highest level of compression that is still readable.

    Just for the sake of it though, here is the abomination of a single function that has the same result:

    const variants = [{"name":"Name 1","variationValues":{"P00003":"mappedValue1"}},{"name":"Name 2","variationValues":{"P00003":"mappedValue2"}},{"name":"Name 3","variationValues":{"P00003":"mappedValue3"}}];
    const variationAttributes = [{"id":"P00003","name":"Variant Name","attributes":[{"name":"Variant Name 1","description":"Variant Description 1","attribute":"mappedValue1"},{"name":"Variant Name 2","description":"Variant Description 2","attribute":"mappedValue2"},{"name":"Variant Name 3","description":"Variant Description 3","attribute":"mappedValue3"}]}];
    
    const getMappedVariants = () =>
        variants.map((variant) => ({
            ...variant,
            variationValues: Object.fromEntries(
                Object.entries(variant.variationValues).map(([id, value]) => [
                    id,
                    variationAttributes
                        .find((v) => v.id === id)
                        ?.attributes?.find?.((a) => a.attribute === value),
                ])
            ),
        }));
    
    console.log(getMappedVariants());