find variant id based on attributes selection - javascript

I have a following product object where it has variants and variants are dependent with attributes. For example, Product A has variants of
size M with color Black, Grey
Size L with color Black
To show it in object format this is how it looks
{
"variants": [{
"id": "1",
"name": "Variant name 1",
"price": null,
"stock": 2,
"variantAttributes": [{
"attribute": {
"id": "2",
"name": "Size"
},
"values": [{
"id": "1",
"name": "Medium",
"value": "M"
}]
},
{
"attribute": {
"id": "3",
"name": "Color"
},
"values": [{
"id": "3",
"name": "Black",
"value": "#000"
}]
}
]
},
{
"id": "2",
"name": "Variant name 2",
"price": null,
"stock": 2,
"variantAttributes": [{
"attribute": {
"id": "2",
"name": "Size"
},
"values": [{
"id": "2",
"name": "Large",
"value": "L"
}]
},
{
"attribute": {
"id": "3",
"name": "Color"
},
"values": [{
"id": "3",
"name": "Black",
"value": "#000"
}]
}
]
}
]
}
Now the problem is I could not find the variant id the user has selected. For e.g if user selects product of size 'M' and of color 'Black' then the variant id should be '1' as per the example I have shared in object.
This is the UI
To show the attributes this is how I have done
function productVariantAttributesPicker(productVariantsAttributes, setSizeGuideOpen, handleColorChange,handleSizeChange,rest) {
return Object.keys(productVariantsAttributes).map(
(productVariantsAttributeId,i) => {
const productVariantsAttribute =
productVariantsAttributes[productVariantsAttributeId];
const { slug, id } = productVariantsAttribute.attribute;
if (slug === "color") {
return <ColorPicker key={i} colorVariant={productVariantsAttributes[id]} handleColorChange={handleColorChange} selectedColor={rest.selectedColor}/>;
}
if (slug === "size") {
return (
<SizePicker
key={i}
sizeVariant={productVariantsAttributes[id]}
// setDisplaySizesOpen={rest.setDisplaySizesOpen}
setSizeGuideOpen={setSizeGuideOpen}
handleSizeChange={handleSizeChange}
selectedSize={rest.selectedSize}
/>
);
}
}
);
}
<div>
{productVariantAttributesPicker(
variants,
setSizeGuideOpen,
handleColorChange,
handleSizeChange,
{selectedSize,selectedColor}
)}
{selectedColor && selectedSize && (
<div>
<AddToCart handleAddToCart={handleAddToCart}></AddToCart>
</div>
)}
</div>
Can anyone help me to find variant id based on attributes selected by user where the selected atrributes is in such form?
const selected_size = {id: "1", name: "Medium", value: "M"}
const selected_color = {id: "3", name: "Black", value: "#000""}

If I understand correctly, you need to find a variant which its variantAttributes contains both selected_size and selected_color.
If so, the logic should is something like:
function hasAttr(variantAttribute, id, value) {
return variantAttribute.find(
variant => {
return variant.values.some((attrValue) => (attrValue.id === id && attrValue.value === value))
}
);
}
const variant = variants.find(
(variant) => {
return hasAttr(variant.variantAttributes, selected_size.id, selected_size.value) &&
hasAttr(variant.variantAttributes, selected_color.id, selected_color.value);
}
);
There is more generic way but I simplified the example to be more clear.
Demo
const variants = [{
id: '1',
name: 'Variant name 1',
price: null,
stock: 2,
variantAttributes: [{
attribute: {
id: '2',
name: 'Size',
},
values: [{
id: '1',
name: 'Medium',
value: 'M',
}, ],
},
{
attribute: {
id: '3',
name: 'Color',
},
values: [{
id: '3',
name: 'Black',
value: '#000',
}, ],
},
],
},
{
id: '2',
name: 'Variant name 2',
price: null,
stock: 2,
variantAttributes: [{
attribute: {
id: '2',
name: 'Size',
},
values: [{
id: '2',
name: 'Large',
value: 'L',
}, ],
},
{
attribute: {
id: '3',
name: 'Color',
},
values: [{
id: '3',
name: 'Black',
value: '#000',
}, ],
},
],
},
];
const selected_size = {
id: '1',
name: 'Medium',
value: 'M'
};
const selected_color = {
id: '3',
name: 'Black',
value: '#000'
};
function hasAttr(variantAttribute, id, value) {
return variantAttribute.find(
variant => {
return variant.values.some((attrValue) => (attrValue.id === id && attrValue.value === value))
}
);
}
const variant = variants.find(
(variant) => {
return hasAttr(variant.variantAttributes, selected_size.id, selected_size.value) &&
hasAttr(variant.variantAttributes, selected_color.id, selected_color.value);
}
);
console.log(variant);

Related

Return name and id property value of all arrays inside object

How to return name and id property value of all arrays? The idea is to make a single map of all these arrays and return the id and name?
Something like this
filters.[key].map((option, index) => (
<ItemFilter key={index}>{option}</ItemFilter>
))
I have this array object
filters: {
"services": [
{
"id": "1b975589-7111-46a4-b433-d0e3c0d7c08c",
"name": "Bank"
},
{
"id": "91d4637e-a17f-4b31-8675-c041fe06e2ad",
"name": "Income"
}
],
"accountTypes": [
{
"id": "1f34205b-2e5a-430e-982c-5673cbdb3a68",
"name": "Digital Account"
}
],
"channels": [
{
"id": "875f8350-073e-4a20-be20-38482a86892b",
"name": "Chat"
}
]
}
You can use flatMap or flat to achieve the desired result.
Object.values(obj.filters).flatMap(v => v)
or
Object.values(obj.filters).flat()
const obj = {
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
};
const result = Object.values(obj.filters).flatMap(v => v);
console.log(result);
If option is referring to name in your example code it could look something like this:
Object.values(
{
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
}.filters
)
.flat()
.map(({ name, index }) => <ItemFilter key={index}>{name}</ItemFilter>);

How to filter array of objects with nested arrays of objects based on other arrays

Sample code.
const cards = [
{
id: "1",
name: "J",
tag: [
{
"colour": "red"
},
{
"colour": "yello"
},
{
"colour": "blue"
},
{
"colour": "white"
},
],
size: [
{
"name": "small"
},
{
"name": "medium"
},
],
},
{
id: "2",
name: "S",
tag: [
{
"colour": "red"
},
{
"colour": "green"
},
{
"colour": "black"
},
],
size: [
{
"name": "small"
},
{
"name": "medium"
},
],
},
{
id: "3",
name: "K",
tag: [
{
"colour": "green"
},
{
"colour": "purple"
},
{
"colour": "brown"
},
{
"colour": "white"
},
],
size: [
{
"name": "large"
},
],
}
];
Test arrays
const sizeArray = ["medium", "small"];
const tagArray = ["red", "black"];
I want the filtered array of objects to only include the second object in this example. Filtering only those who match all criteria leaving me with a new array of objects.
I've tried with .filter, .some, .includes and no luck looking over many other answers to similar questions.
Thanks a lot.
You can use the filter with every and some.
Since you need to filter the result which contains all sizeArray and tagArray in size and tag array respectively.
const cards = [
{
id: "1",
name: "J",
tag: [
{
colour: "red",
},
{
colour: "yello",
},
{
colour: "blue",
},
{
colour: "white",
},
],
size: [
{
name: "small",
},
{
name: "medium",
},
],
},
{
id: "2",
name: "S",
tag: [
{
colour: "red",
},
{
colour: "green",
},
{
colour: "black",
},
],
size: [
{
name: "small",
},
{
name: "medium",
},
],
},
{
id: "3",
name: "K",
tag: [
{
colour: "green",
},
{
colour: "purple",
},
{
colour: "brown",
},
{
colour: "white",
},
],
size: [
{
name: "large",
},
],
},
];
const sizeArray = ["medium", "small"];
const tagArray = ["red", "black"];
const result = cards.filter(({ tag, size }) =>
sizeArray.every((s) => size.some((si) => si.name === s)) &&
tagArray.every((t) => tag.some((ta) => ta.colour === t))
);
console.log(result);
As you filter the main array, you want to make sure at least one of the tag values is in the tagArray and at least one of the size values is in the sizeArray. This can be achieved using includes.
const cards=[{id:"1",name:"J",tag:[{colour:"red"},{colour:"yello"},{colour:"blue"},{colour:"white"}],size:[{name:"small"},{name:"medium"}]},{id:"2",name:"S",tag:[{colour:"red"},{colour:"green"},{colour:"black"}],size:[{name:"small"},{name:"medium"}]},{id:"3",name:"K",tag:[{colour:"green"},{colour:"purple"},{colour:"brown"},{colour:"white"}],size:[{name:"large"}]}];
const sizeArray = ["medium", "small"];
const tagArray = ["red", "black"];
const filtered = cards.filter((card) => {
return (
card.tag.some((tag) => tagArray.includes(tag.colour)) &&
card.size.some((size) => sizeArray.includes(size.name))
);
});
console.log(filtered);
Note that includes isn't super efficient, but I'm assuming you're not working with tons of data here. If your sizeArray or tagArray are especially large, you could look into something like the Set object for hash table efficiency.

How to get difference between two array of object with different property comparer?

I have these two arrays:
main:
[
{ id: "1"},
{ id: "2"},
{ id: "3"}
]
filtered:
[
{ id: "80", link_id: "1"},
{ id: "50", link_id: null},
{ id: "67", link_id: "3"}
]
I need to get the items of main which have as id those contained in filtered with the property: link_id, I tried with:
main.filter(x => filtered.includes(x.id));
the problem is that this will return null, and also this doesn't allow me to check if link_id is null
var main = [{
id: "1"
},
{
id: "2"
},
{
id: "3"
}
],
filtered = [{
id: "80",
link_id: "1"
},
{
id: "50",
link_id: null
},
{
id: "67",
link_id: "3"
}
],
result = main.filter(x =>
filtered.includes(x.id)
);
console.log(result)
Try with some() method
var main = [
{ id: "1"},
{ id: "2"},
{ id: "3"}
]
var filtered = [
{ id: "80", link_id: "1"},
{ id: "50", link_id: null},
{ id: "67", link_id: "3"}
]
console.log(main.filter(x => filtered.some(item => item.link_id === x.id) ));
you are close, basically you need to check in each item of the filtered array.
includes is more for a plain object as the documentation states.
check the snippet below, you can use findIndex, find or some to get if the element exist on the filtered array.
const main = [{
id: "1"
},
{
id: "2"
},
{
id: "3"
}
]
const filtered = [{
id: "80",
link_id: "1"
},
{
id: "50",
link_id: null
},
{
id: "67",
link_id: "3"
}
]
const resultFindIndex = main.filter(item => -1 !== filtered.findIndex(filteredItem => item.id === filteredItem.link_id))
const resultFind = main.filter(item => filtered.find(filteredItem => item.id === filteredItem.link_id))
const resultSome = main.filter(item => filtered.some(filteredItem => item.id === filteredItem.link_id))
console.log(resultFindIndex)
console.log(resultFind)
console.log(resultSome)

add value to inner array using map in javascript

Hello suppose I have the following array:
let array = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
},
{
subid: "11",
name: "name",
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
},
{
subid: "21",
name: "name",
}
]
}
]
My goal is to take the id of each of the objects and add it to the inner array categories. So it would look like this:
let array = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
id: "1"
},
{
subid: "11",
name: "name",
id: "1"
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
id: "2"
},
{
subid: "21",
name: "name",
id: "2"
}
]
}
]
Here is what I have so far:
array.map(x => (x.id)) // returns new array of ids
// add these into the categories
Can someone help me figure this out using map? If map cant be used I think for each will work as well
With map method and spread syntax inside object you could do this.
let array = [{"id":"1","name":"name","categories":[{"subid":"10","name":"name"},{"subid":"11","name":"name"}]},{"id":"2","name":"name","categories":[{"subid":"20","name":"name"},{"subid":"21","name":"name"}]}]
let result = array.map(({id, categories, ...rest}) => ({
...rest, id, categories: categories.map((o) => ({...o, id}))
}))
console.log(result)
You can use Array.forEach() to iterate over the array and then use Array.map() on categories array to add the id prop to all its objects:
let array = [ { id: "1", name: "name", categories: [ { subid: "10", name: "name", }, { subid: "11", name: "name", } ] }, { id: "2", name: "name", categories: [ { subid: "20", name: "name", }, { subid: "21", name: "name", } ] } ];
array.forEach((o)=>{
o.categories = o.categories.map(cat=>Object.assign({},cat,{id : o.id}));
});
console.log(array);
What about nested map?
let arr = [
{
id: "1",
name: "name",
categories: [
{
subid: "10",
name: "name",
},
{
subid: "11",
name: "name",
}
]
},
{
id: "2",
name: "name",
categories: [
{
subid: "20",
name: "name",
},
{
subid: "21",
name: "name",
}
]
}
]
arr.map(x=>{
x.categories.map(y => {
y.id = x.id
})
})
console.log(arr)
You need to map all arrays with copied properties to get a new independent data with a new property.
let array = [{ id: "1", name: "name", categories: [{ subid: "10", name: "name", }, { subid: "11", name: "name", }] }, { id: "2", name: "name", categories: [{ subid: "20", name: "name", }, { subid: "21", name: "name", }] }],
updated = array.map(o => Object.assign(
{},
o,
{ categories: o.categories.map(p => Object.assign({}, p, { id: o.id })) }
));
console.log(updated);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using each of Jquery should do the job.
$.each(array,function(index,item){item.categories.id = item.id;});
Thanks.

Group by each item in an array, using lodash

I have these kind of data structure from an API, and they told me to group them accordingly.
INPUT
{
0: {
id: 0,
name: 'foo',
categories: [
'Category001',
'Category002/sub-category001'
]
},
1: {
id: 1,
name: 'bar',
categories: [
'Category002/sub-category001'
]
},
2: {
id: 2,
name: 'bazz',
categories: [
'Category001',
'Category002',
'Category003'
]
},
3: {
id: 3,
name: 'rem',
categories: [
'Category001/sub-category002/nth-category008',
'Category001/sub-category004',
'Category003/sub-category001'
]
}
}
DESIRED OUTPUT
{
0: {
"name": "Category001",
"isCategory": true,
"children": [
{
"id": 0,
"name": "foo",
"categoryPath": "Category001",
"isCategory": false
},
{
"id": 2,
"name": "bazz",
"categoryPath": "Category001",
"isCategory": false
},
{
"name": "sub-category004",
"categoryPath": "Category001/sub-category004",
"children": [
{
"id": 3,
"name": "rem",
"isCategory": false,
}
],
"isCategory": true
},
{
"name": "sub-category002",
"categoryPath": "Category001/sub-category002",
"children": [
{
"name": "sub-category008",
"categoryPath": "Category001/sub-category002/nth-category008",
"children": [
{
"id": 3,
"name": "rem",
"isCategory": false
}
],
"isCategory": true
},
],
"isCategory": true
},
{
"name": "sub-category002",
"categoryPath": "Category001/sub-category002",
"isCategory": true
}
],
"categoryPath": ""
},
1: {
"name": "Category002",
"isCategory": true,
"children": [
{
"id": 2,
"name": "bazz",
"categoryPath": "Category002",
"isCategory": false
},
{
"name": "sub-category001",
"categoryPath": "Category002/sub-category001",
"children": [
{
"id": 0,
"name": "foo",
"isCategory": false,
},
{
"id": 1,
"name": "bar",
"isCategory": false,
}
],
"isCategory": true
}
],
"categoryPath": ""
},
2: {
"name": "Category003",
"isCategory": true,
"children": [
{
"id": 2,
"name": "bazz",
"categoryPath": "Category002",
"isCategory": false
},
{
"name": "sub-category001",
"categoryPath": "Category003/sub-category001",
"children": [
{
"id": 0,
"name": "foo",
"isCategory": false,
}
],
"isCategory": true
}
],
"categoryPath": ""
}
}
Question
Is there an easy way of doing it in lodash?
A simple groupby won't do it though, LOL
var groups = _.chain(items)
.groupBy('categories')
.pairs()
.value();
Custom processing can not be avoided here in addition to lodash functions. The following is an attempt to use lodash at most:
var transformed = _(input)
.transform(function (result, item) {
_(item.categories)
.map(function (categoryPath) {
return categoryPath.split('/');
})
.each(function (categoryPathParts) {
var dict = result;
var par;
var fullpath = _.reduce(categoryPathParts, function (path, category, i) {
path += (i > 0 ? '/' : '') + category;
if (!(par = _.find(dict, 'name', category))) {
dict.push(par = {
name: category,
categoryPath: (i > 0 ? path : ''),
isCategory: true,
children: []
});
}
dict = _.find(dict, 'name', category).children;
return path;
}, "")
par.children.push({
id: item.id,
name: item.name,
isCategory: false,
categoryPath: fullpath,
});
}).value();
}, [])
.transform(function (resObj, resCat, i) {
resObj[i] = resCat;
}, {});
var input = {
0: {
id: 0,
name: 'foo',
categories: [
'Category001',
'Category002/sub-category001']
},
1: {
id: 1,
name: 'bar',
categories: [
'Category002/sub-category001']
},
2: {
id: 2,
name: 'bazz',
categories: [
'Category001',
'Category002',
'Category003']
},
3: {
id: 3,
name: 'rem',
categories: [
'Category001/sub-category002/nth-category008',
'Category001/sub-category004',
'Category003/sub-category001']
}
};
var transformed = _(input)
.transform(function (result, item) {
_(item.categories)
.map(function (categoryPath) {
return categoryPath.split('/');
})
.each(function (categoryPathParts) {
var dict = result;
var par;
var fullpath = _.reduce(categoryPathParts, function (path, category, i) {
path += (i > 0 ? '/' : '') + category;
if (!(par = _.find(dict, 'name', category))) {
dict.push(par = {
name: category,
categoryPath: (i > 0 ? path : ''),
isCategory: true,
children: []
});
}
dict = _.find(dict, 'name', category).children;
return path;
}, "")
par.children.push({
id: item.id,
name: item.name,
isCategory: false,
categoryPath: fullpath,
});
}).value();
}, [])
.transform(function (resObj, resCat, i) {
resObj[i] = resCat;
}, {});
document.getElementById('resultArea').textContent = JSON.stringify(transformed, null, 2);
textarea {
width: 100%;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.min.js"></script>
<textarea id="resultArea" rows="111" ></textarea>

Categories