Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic selection of product variants, comparing 2 arrays

Sorry for the bad title, couldn't figure a better one.

Product Options:

[
  {
    "_id": "5cdd1e81e85ecc7ebb145039",
    "name": "Sizes S/M - M/L - L/XL ",
    "display_name": "Size",
    "display_style": "rectangle",
    "values": [
      {
        "_id": "5cdd1e81e85ecc7ebb14503a",
        "label": "Small / Medium",
        "option_id": "5cdd1e81e85ecc7ebb145039"
      },
      {
        "_id": "5cdd1e81e85ecc7ebb14503b",
        "label": "Large / X-Large",
        "option_id": "5cdd1e81e85ecc7ebb145039"
      },
      {
        "_id": "5da8080ea51d8207e9098875",
        "label": "Medium / Large",
        "option_id": "5cdd1e81e85ecc7ebb145039"
      }
    ]
  },
  {
     "_id": "5cdd280ce85ecc7ebb145040",
    "name": "Hat Sizes Νο 56-57 / 58-59 / 60-61",
    "display_name": "Hat Size",
    "display_style": "rectangle",
    "values": [
      {
        "_id": "5cdd280ce85ecc7ebb145041",
        "label": "56 / 57",
        "option_id": "5cdd280ce85ecc7ebb145040"
      },
      {
        "_id": "5cdd280ce85ecc7ebb145042",
        "label": "58 / 59",
        "option_id": "5cdd280ce85ecc7ebb145040"
      },
      {
        "_id": "5cdd280ce85ecc7ebb145043",
        "label": "60 / 61 ",
        "option_id": "5cdd280ce85ecc7ebb145040"
      }
    ]
  }
]

Product Variants:

[
  {
    "_id": "5e0a02e4413f9e12f20edfb5",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd1e81e85ecc7ebb14503a",
          "label": "Small / Medium",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145041",
          "label": "56 / 57",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  },
  {
    "_id": "5e0a02e4413f9e12f20edfb6",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",

          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd1e81e85ecc7ebb14503a",
          "label": "Small / Medium",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145042",
          "label": "58 / 59",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  },
  {
    "_id": "5e0a02e4413f9e12f20edfb9",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd1e81e85ecc7ebb14503b",
          "label": "Large / X-Large",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145042",
          "label": "58 / 59",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  },
  {
    "_id": "5e0a02e4413f9e12f20edfba",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd1e81e85ecc7ebb14503b",
          "label": "Large / X-Large",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145043",
          "label": "60 / 61 ",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  },
  {
    "_id": "5e0a02e4413f9e12f20edfbc",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5da8080ea51d8207e9098875",
          "label": "Medium / Large",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145042",
          "label": "58 / 59",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  },
  {
    "_id": "5e0a02e4413f9e12f20edfbb",
    "options": [
      {
        "option": {
          "_id": "5cdd1e81e85ecc7ebb145039",
          "display_name": "Size",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5da8080ea51d8207e9098875",
          "label": "Medium / Large",
          "option_id": "5cdd1e81e85ecc7ebb145039"
        }
      },
      {
        "option": {
          "_id": "5cdd280ce85ecc7ebb145040",
          "display_name": "Size Hat",
          "display_style": "rectangle"
        },
        "value": {
          "_id": "5cdd280ce85ecc7ebb145041",
          "label": "56 / 57",
          "option_id": "5cdd280ce85ecc7ebb145040"
        }
      }
    ]
  }
]

I have this code (part of React component):

setVariant = (optionIndex, valueIndex) => {
    const { product } = this.state;
    const parentProduct = { ...product };

    const optionValue = parentProduct.options[optionIndex].values[valueIndex];

    const emptyArr = [];

    parentProduct.variants.forEach(({ options }) => {
      options.forEach((item) => {
        if (item.value._id === optionValue._id) {
          emptyArr.push(...options);
        }
      });
    });

    const updatedOptions = parentProduct.options.map((productOption) => {
      const productOptions = JSON.parse(JSON.stringify(productOption));

      productOptions.values = productOptions.values.map((productOptionValue) => {
        productOptionValue.disabled = emptyArr.every(
          ({ value: { _id } }) => productOptionValue._id !== _id
        );
        return productOptionValue;
      });
      return productOptions;
    });

My issue with this snippet it that it sets the disabled property correctly ex. if I choose first Size e.x. Small/Medium, then Size Hat 60 / 61 gets disabled and I can click the other two options. But if I click another Size Hat 58 / 59 then Large / X-Large won't get enabled.

This is what I'm trying to achieve:

desired result

But this is what i'm getting: (when changing 56 / 57 etc. then Sizes ex. Large/X-Large should be enabled if found in variants array, red color represents disabled button)

enter image description here

like image 470
Stathis Ntonas Avatar asked Dec 31 '19 11:12

Stathis Ntonas


1 Answers

You can create a map for every option's value which would have a reference on the corresponding values of other options. For example, a map for Size values should look like:

{
  sizeValueIdA: [hatSizeValueA, hatSizeValueB],
  sizeValueIdB: [hatSizeValueB],
  sizeValueIdC: []
}

So with this map, you can easily get a list of available Hat Size values knowing selected Size's value.

This is an example of code that allows you to get a list of Hat Size values by Size value id and vice versa. I think that with this data you can easily disable options in UI if they not in these lists

const sizeToHatSize = convertOptionToValueMap(productOptions[0]);
const hatSizeToSize = convertOptionToValueMap(productOptions[1]);

productVariants.forEach(variant => {
  const sizeValueId = variant.options[0].value._id;
  let hatSizeValueId = variant.options[1].value._id;
  sizeToHatSize[sizeValueId].push(hatSizeValueId);
  hatSizeToSize[hatSizeValueId].push(sizeValueId);
});

function convertOptionToValueMap(option) {
  return option.values.reduce((dict, sizeOptionValue) => {
    dict[sizeOptionValue._id] = [];
    return dict;
  }, {});
}

describe('59543685', function() {
  it('should return available Hat Sizes for selected Size', function() {
    const sizeValueId = '5cdd1e81e85ecc7ebb14503a';
    expect(sizeToHatSize[sizeValueId]).toEqual([
      '5cdd280ce85ecc7ebb145041',
      '5cdd280ce85ecc7ebb145042',
    ]);
  });

  it('should return available Sizes for selected Hat Size', function() {
    const hatSizeValueId = '5cdd280ce85ecc7ebb145041';
    expect(hatSizeToSize[hatSizeValueId]).toEqual([
      '5cdd1e81e85ecc7ebb14503a',
      '5da8080ea51d8207e9098875'
    ]);
  });
});

In my example, I have used arrays but you can choose a map instead of array for fast checking that an option is available.

const siteToHatSize = {
  sizeValueIdA: {hatSizeValueA: true, hatSizeValueB: true},
  sizeValueIdB: {hatSizeValueB: true},
  sizeValueIdC: {}
}

const isEnabled = siteToHatSize[selectedSizeValueId][hatSizeId] // you can use it in render function

Also, in your case, with only 2 options you can also look one the problem as a 2-dimensional matrix. Every variant is just an intersection between row(Hat Size's value) and Column(Size's value).

[          /*Small*/ /*Medium*/ /*Large*/ 
 /*56-57*/ [     1,          0,        1],
 /*58-59*/ [     1,          1,        0],
 /*60-61*/ [     0,          0,        1],
]

Maybe this would be more appropriate structure for you :)

like image 50
maksimr Avatar answered Nov 06 '22 01:11

maksimr