How can I generate combinations of objects? - javascript

I have a page which allows a user to create option groups, and for each option group create a series of options.
E.g.
Colour
* Green
* Blue
* Red
Size
* Small
* Medium
* Large
Where colour and size are the option groups.
The user can create any number of groups and options.
What I'm struggling to find is a way of creating a list of all the possible variations based on the number of options available.
Ideally what I want is an object with a property called "options" which is an array containing the options that this variation is composed of e.g.
[
{ options : [ { name: "Green" } , { "name" : "Small" } ] },
{ options : [ { name: "Green" } , { "name" : "Medium" } ] },
{ options : [ { name: "Green" } , { "name" : "Large" } ] },
{ options : [ { name: "Blue" } , { "name" : "Small" } ] },
{ options : [ { name: "Blue" } , { "name" : "Medium" } ] },
{ options : [ { name: "Blue" } , { "name" : "Large" } ] },
{ options : [ { name: "Red" } , { "name" : "Small" } ] },
{ options : [ { name: "Red" } , { "name" : "Medium" } ] },
{ options : [ { name: "Red" } , { "name" : "Large" } ] },
]
I can imagine that some level of recursion is required but I'm really struggling to write the correct JavaScript code.
Any help is greatly appreciated. Thanks in advance
EDIT: Looks like I didn't explain myself correctly, apologies for that. I can see that some of the solutions provided loop through the colours first and then the sizes, however, as mentioned above the user can create any number of groups whereas those solutions are only limited to 2 groups e.g there might be more option groups like "shoe size", "storage capacity", "screen size" etc
The problem I'm trying to solve is to create stock variations on an e-commerce site, where each product can have different options available.
The final structure will contain more data that I've given in the example above, so more like:
[
{ sku: "PRODUCT1-GRSM", options : [ { name: "Green" } , { "name" : "Small" } ] },
{ sku: "PRODUCT1-GRMD", options : [ { name: "Green" } , { "name" : "Medium" } ] },
{ sku: "PRODUCT1-GRLG", options : [ { name: "Green" } , { "name" : "Large" } ] },
{ sku: "PRODUCT1-BLSM", options : [ { name: "Blue" } , { "name" : "Small" } ] },
{ sku: "PRODUCT1-BLMD", options : [ { name: "Blue" } , { "name" : "Medium" } ] },
{ sku: "PRODUCT1-BLLG", options : [ { name: "Blue" } , { "name" : "Large" } ] },
{ sku: "PRODUCT1-RESM", options : [ { name: "Red" } , { "name" : "Small" } ] },
{ sku: "PRODUCT1-REMD", options : [ { name: "Red" } , { "name" : "Medium" } ] },
{ sku: "PRODUCT1-RELG", options : [ { name: "Red" } , { "name" : "Large" } ] },
]
That data will be bound to a table which contains input fields for each generated variation, but it's generating those variations which is the problem. Thanks

May be this is what you are asking:
var color = ["red","blue"];
var size = ["Small","Medium","Large"];
var options = [];
for(var i=0;i<color.length;i++)
{
for(var j = 0;j<size.length;j++)
{
var item = [];
item.push({name:color[i]},{name:size[j]});
options.push({options:item});
}
}
console.log(options);//options will contain your desired result
You can dynamically add color, size in color and size array respectively and you will get the desired output.

This is very old but in case you want to support any number of attributes:
function add_variations_to_array(base, variations){
let ret = [];
for(let e of base)
for(let variation of variations){
ret.push(e+" - "+variation);
}
return ret;
}
//Size, color, material
let attributes = [["S","M","L","XL"],["Red","Green","Blue"],["Wool","Cotton","Steel"]];
//generate variations
let variations = attributes[0];
if(attributes.length > 1){
for(let i in attributes)
if(i>0)
variations = add_variations_to_array(variations, attributes[i]);
}
console.log(variations);
https://jsfiddle.net/pswkzLga/

Related

Transform an explicit JSON payload into an array driven generic payload?

I am currently working in a project that has insisted in explicitly defining over 1,700 questions into a JSON data schema for an API and its getting out of control. I have been suggesting a more generic structure to the schema and let the data do the talking to tell you what the context is.
Whilst there are debates happening around which schema to use, we have decided on our internal systems, to go ahead and use a more generic model even if the externally facing model is the explicit one. This means we need an adapter to convert from one to the other, until such time as we can just use the one we wanted in the first place.
The business is a Java shop, I don't know whether to advise to build the adapter in Java or whether we can incorporate some lightweight JavaScript to do the work, maybe in the form of a configuration.
My question is: How would you approach converting the first JSON example into the second JSON example? It maps from explicitly defined objects to generic objects in arrays. Thanks for considering my question.
Example One
{
"nested_object" : {
"department_one" : {
"floor" : "4",
"product_one" : {
"quantity" : 10,
"size" : "L"
},
"product_two" : {
"quantity" : 5,
"size" : "S"
}
},
"department_two" : {
"floor" : "2",
"product_thirteen" : {
"quantity" : 1,
"size" : "M"
},
"product_eleven" : {
"quantity" : 8,
"size" : "L"
}
}
}
}
Example Two
{
"departments" : [
{
"department_name" : "department_one",
"floor" : "4",
"products" : [
{
"product_name" : "product_one",
"quantity" : 10,
"size" : "L"
},
{
"product_name" : "product_two",
"quantity" : 5,
"size" : "S"
}
]
},
{
"department_name" : "department_two",
"floor" : "2",
"products" : [
{
"product_name" : "product_thirteen",
"quantity" : 1,
"size" : "M"
},
{
"product_name" : "product_eleven",
"quantity" : 8,
"size" : "L"
}
]
}
]
}
You could use a combination of Object.keys (to grab product and department names). Below is a quick implementation.
const obj1 = {
"nested_object" : {
"department_one" : {
"floor" : "4",
"product_one" : {
"quantity" : 10,
"size" : "L"
},
"product_two" : {
"quantity" : 5,
"size" : "S"
}
},
"department_two" : {
"floor" : "2",
"product_thirteen" : {
"quantity" : 1,
"size" : "M"
},
"product_eleven" : {
"quantity" : 8,
"size" : "L"
}
}
}
}
const transformedObj = {
departments: [ ],
};
//holds all department names
const departmentKeys = Object.keys(obj1.nested_object)
const departmentsArr = departmentKeys.map((key) => {
const floor = obj1.nested_object[key].floor
//remove floor reference, since we already stored the value above
delete obj1.nested_object[key].floor
//holds all product names
const productsKeysArr = Object.keys(obj1.nested_object[key])
//holds all product objects for respective department
const productsArr = productsKeysArr.map((product) => {
const quantity = obj1.nested_object[key][product].quantity
const size = obj1.nested_object[key][product].size
return {
product_name: product,
quantity: quantity,
size: size
}
})
return {
department_name: key,
floor: floor,
products: productsArr
}
})
//assign departments array to transformed object
transformedObj.departments = departmentsArr
console.log(transformedObj)
This would be my take on this. I like conciseness and expressiveness in implementations:
const data = { "nested_object": { ... }}
Object.entries(data.nested_object).map(([department_name, {floor, ...ps}]) => ({
department_name,
floor,
products: Object.entries(ps).map(([product_name, p]) => ({product_name, ...p}))
}))

I want to compare inner array in JSON array of object and return new array depending on condition using JavaScript

I have following array of object
let studentArray =
[{
"name" : "Computer Science",
"students" : [
{
"student_name" : "A"
},
{
"student_name" : "B"
}
]
},
{
"name" : "Math",
"students" : [
{
"student_name" : "A"
},
{
"student_name" : "B"
},
{
"student_name" : "C"
}
]
}]
and I want answer like below.
[
{
"student_name" : "A",
"courses": ["Computer Science", "Math"]
},
{
"student_name" : "B",
"courses": ["Computer Science", "Math"]
},
{
"student_name" : "C",
"courses": ["Math"]
}
]
Please help with javascript functionality and according to data structure algorithm.
I have tried below it is not working.
I there any another way to doing this Using different another loops or something another logic for that.
let studentArray = [{
"name": "Computer Science",
"students": [{
"student_name": "A"
},
{
"student_name": "B"
}
]
},
{
"name": "Math",
"students": [{
"student_name": "A"
},
{
"student_name": "B"
},
{
"student_name": "C"
}
]
}
]
studentArray.forEach((item, index) => {
//console.log(item);
if (index > 0) {
console.log("Previous: " + studentArray[index - 1].students);
}
if (index < studentArray.length - 1) {
console.log("Next: " + studentArray[index + 1].students);
}
//console.log(studentArray);
console.log(item.students.filter(comparer(item.students)));
});
function comparer(otherArray) {
return function(current) {
return otherArray.filter(function(other) {
return other.value == current.value && other.display == current.display
}).length == 0;
}
}
You can use Array.reduce() on the studentArray to group students with their courses.
We create an object keyed by student name and iterate over each course's student array to add students to the map (using for...each).
Finally, we use Object.values() to turn our map into an array:
const studentArray = [{ "name" : "Computer Science", "students" : [ { "student_name" : "A" }, { "student_name" : "B" } ] }, { "name" : "Math", "students" : [ { "student_name" : "A" }, { "student_name" : "B" }, { "student_name" : "C" } ] }];
const result = Object.values(studentArray.reduce((acc, course) => {
for(let student of course.students) {
let student_name = student.student_name;
acc[student_name ] = acc[student_name ] || { student_name , courses: []};
acc[student_name ].courses.push(course.name);
}
return acc;
}, {}))
console.log(result)
Use a nested forEach loop
const studentArray = [{
name: "Computer Science",
students: [{
student_name: "A"
},
{
student_name: "B"
}
]
},
{
name: "Math",
students: [{
student_name: "A"
},
{
student_name: "B"
},
{
student_name: "C"
}
]
}
];
const newArr = [];
studentArray.forEach((c) => {
c.students.forEach((s) => {
let studentIndex = newArr.findIndex(el => el.student_name === s.student_name);
studentIndex === -1 ? newArr.push({
student_name: s.student_name,
courses: [c.name]
}) : newArr[studentIndex].courses.push(c.name)
})
})
console.log(newArr);
Another approach using reduce, map, and some ES6 spread syntax:
const courses = [
{
"name" : "Computer Science",
"students" : [{ "student_name" : "A" }, { "student_name" : "B" }]
},
{
"name" : "Math",
"students" : [{ "student_name" : "A" }, { "student_name" : "B" }, { "student_name" : "C" }]
}
]
// Add students from a course to an array if they're not present already
const selectUniqueStudents = (currentStudentList, course) =>
currentStudentList.concat(course.students.filter(newStudent =>
currentStudentList.every(
currentStudent => currentStudent.student_name !== newStudent.student_name
)
))
// Add each course that the student is on to an array and append to the
// student object
const addCourseDetails = (student) => ({
...student,
courses: courses
.filter(course =>
course.students.some(courseStudent => courseStudent.student_name === student.student_name)
)
.map(course => course.name)
})
const transformedResult = courses
.reduce(selectUniqueStudents, [])
.map(addCourseDetails)
console.log(transformedResult)
// Returns:
//
// [
// { student_name: 'A', courses: [ 'Computer Science', 'Math' ] },
// { student_name: 'B', courses: [ 'Computer Science', 'Math' ] },
// { student_name: 'C', courses: [ 'Math' ] }
// ]
Same as with Vineet's answer, Terry's would run faster. This is not as concise or easy to read either. But the demonstration of aggregate array functions and ES6 syntax might be useful.

How to transform this specific js array into js object?

I have one javascript array got from back end api, for convenience, need to be sort into the form of below, described as final target.
But I don't know how to start. Anyone can help?
The original src array is like below :
var src = [
{
"parent_kind" : "Animal",
"name" : "Cow"
},
{
"name" : "Animal"
},
{
"parent_kind" : "Animal",
"name" : "Dog"
},
{
"parent_kind" : "Animal",
"name" : "Horse"
},
{
"name" : "Vehicle"
},
{
"parent_kind" : "Vehicle",
"name" : "Bus"
},
{
"parent_kind" : "Bus",
"name" : "Shuttle"
},
]
The final target is :
{
"Vehicle" : {
"Bus" : {
"Shuttle" : {}
}
},
"Animal" : {
"Cow" : {},
"Dog" : {},
"Horse" : {}
}
}
I can got each element of the original array by
for (let ele of src) {
console.log(ele)
}
you can do that with a simple Array.reduce() method
var src =
[ { parent_kind: 'Animal', name: 'Cow' }
, { name: 'Animal' }
, { parent_kind: 'Animal', name: 'Dog' }
, { parent_kind: 'Animal', name: 'Horse' }
, { name: 'Vehicle' }
, { parent_kind: 'Vehicle', name: 'Bus' }
, { parent_kind: 'Bus', name: 'Shuttle' }
]
let res = src.reduce((a,{parent_kind,name},i)=>
{
if(!!parent_kind)
{
let np = a.p.find(x=>x.pN===parent_kind)
if(!np)
{
a.r[parent_kind] = {}
np = {pN:parent_kind, e:a.r[parent_kind]}
a.p.push( np )
}
let z = np.e[name] = {}
a.p.push( {pN:name, e:z} )
}
return (i===a.len)? a.r : a
}
,{len:src.length-1,r:{},p:[]})
console.log( res )
.as-console-wrapper { max-height: 100% !important; top: 0; }
if you can ok with loop the src many times :-( That's performance is not a concern.
var src = [{
"parent_kind": "Animal",
"name": "Cow"
},
{
"name": "Animal"
},
{
"parent_kind": "Animal",
"name": "Dog"
},
{
"parent_kind": "Animal",
"name": "Horse"
},
{
"name": "Vehicle"
},
{
"parent_kind": "Vehicle",
"name": "Bus"
},
{
"parent_kind": "Bus",
"name": "Shuttle"
},
];
const fn = (source, result, parent) => {
const children = source.filter(({
parent_kind
}) => parent_kind === parent).map(({
name
}) => name);
children.forEach(c => result[c] = {});
children.forEach(c => fn(source, result[c], c));
}
result = {};
fn(src, result)
console.log(result);

mongo update script: change fields in embedded documents

I am writing a script to update(add and rename) certain fields, which are part of an embedded document within the ones stored in the db collection.
to give an example document:
{
_id: UUID("025bda29-0d09-4923-8f7f-ea2e825be2c8"),
name: "test",
sets: [
{
"name": "1",
"values": [
{
name: "a",
value: 5
}
]
},
{
"name": "2",
"values": [
{
name: "a",
value: 3
}
]
}
]
}
This is my script:
function convertGroup (group) {
for (var i = 0; i < group.sets.length; i++) {
var set = group.sets[i];
var oldValuesField = "sets." + i.toString() + ".values";
var mainValuesField = "sets." + i.toString() + "mainValues";
var additionalValuesField = "sets." + i.toString() + ".additionalValues";
db.getCollection('group').updateOne({
"_id" : group._id
}, {
$set: {
mainValuesField : set.values,
additionalValuesField : [ ]
},
$unset: {
oldValuesField: ""
}
});
}
}
db.getCollection('group').find({'sets.0.mainValues': {$exists: false}}).forEach(convertGroup);
According to the documentation the $rename does not work on arrays, that is why I used set and unset.
what happens when I run this code, is that I get the mainValues field and additionalValues field in the group document, and not in the set documents.
this is what I want it to become:
{
_id: UUID("025bda29-0d09-4923-8f7f-ea2e825be2c8"),
name: "test",
sets: [
{
"name": "1",
"mainValues": [
{
name: "a",
value: 5
}
],
"additionalValues": [ ]
},
{
"name": "2",
"mainValues": [
{
name: "a",
value: 3
}
],
"additionalValues": [ ]
}
]
}
Can anyone explain to me why this happens and how I can make this work the way I want it to?
I managed to fix it by rewriting the script like this:
function convertGroup(group) {
group.sets.forEach(function (set) {
if (!set.mainValues) {
$set : {
set.mainValues = set.values
}
}
if (!set.additionalValues) {
$set : {
set.additionalValues = []
}
}
});
db.getCollection('group').update({'_id': group._id}, group);
}
db.getCollection('group').find({'sets.mainValues': {$exists: false}}).forEach(convertGroup)
the different is mostly not using the notation 'sets.[index].values' but editing it directly on the json and using 'update' instead of 'updateOne'

Simplify forEach statement to update a mongoDB document with nested objects and arrays

I'd like to update the value of the key shouldSendAlert in the following document:
{
"_id" : ObjectId("5c61c4db46d18e1092c5b024"),
"service" : "SRVPVD",
"menu" : [
{
"sub" : [
{
"options" : [
{
"item" : [
{
"name" : "",
"actions" : [
{
"name" : "communicateClient",
"value" : true
},
{
"name" : "shouldSendAlert",
"value" : false
}
]
}
],
"name" : "Technology Support"
},
{
"item" : [
{
"name" : "",
"actions" : [
{
"name" : "communicateClient",
"value" : true
}
]
}
],
"name" : "Company Support"
}
],
"name" : "Support"
},
{
"name" : " FAQ"
}
],
"name" : "Help"
}
]
}
I've managed to do this, querying the document using a multiple $elemMatch query, and using forEach to run through the nested arrays in order to alter the value of shouldSendAlert:
{
let menuItems = db.getCollection('menumodels').find({menu: {$elemMatch: {name: 'Help',sub: {$elemMatch: {name: 'Support',motivos: {$elemMatch: {name: 'Technology Support'}}}}}}});
menuItems.forEach((r) => {
r.menu.forEach(menuItem => {
if (menuItem.name == 'Help') {
menuItem.sub.forEach(sub => {
if (sub.name == 'Support') {
sub.motivos.forEach(motivo => {
if (motivo.name == "Technology Support") {
motivo.item[0].actions.forEach(action => {
if (action.name == 'shouldSendAlert') {
action.value = true;
db.getCollection('menumodels').update({_id: r._id}, {$set: {menu: r.menu}})
}
})
}
})
}
})
}
})
});
}
Is it - regarding performance - necessary, to code this MongoDB query
or update logic into a smarter form? Does the multiple use of $elemMatch affect performance?

Categories

Resources