I'm struggling with adding JSON to existing objects with Vue.js.
So I have a function that is calculating some variants and it's working well for now, but I would need to change them in order to give correct data to my Laravel backend.
So this is how my array looks like (this array is used to calculate variants)
"attributes":[
{
"title":{
"text":"Size",
"value":"1"
},
"values":[
"Red",
"Green"
]
},
{
"title":{
"text":"Color",
"value":"2"
},
"values":[
"X",
"M"
]
}
]
And I'm calculating them like this:
addVariant: function () {
const parts = this.form.attributes.map(attribute => attribute.values);
const combinations = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
this.form.variants = combinations;
},
And the output looks like:
"variants":[
[
"Red",
"X"
],
[
"Red",
"M"
],
[
"Green",
"X"
],
[
"Green",
"M"
]
]
So this is calculated well, but this variants would need to look like this:
"variants":[
{
"title": "Red/M",
"slug": "prooooo",
"options": [
{
"key": "Color",
"value": "Red"
},
{
"key": "Size",
"value": "M"
}
]
},
{
"title": "Red/X",
"slug": "prooooo",
"options": [
{
"key": "Color",
"value": "Red"
},
{
"key": "Size",
"value": "X"
}
]
}
As you see I have a title field that is calculated by options.values, but the real question is how to make that JSON look like the last given JSON.
Any ideas on how to do it?
From what I see, the missing values to create the final output are the key properties of the options arrays, which I see in the first attributes array under the text property of the title object.
The first change that you can do is on the creation of the parts variable: instead of returning the attribute.values directly, map over them to save the attribute.title.text as well.
At the same time, we set the keys of the returned object to match the options object from the desired output.
const parts = this.form.attributes.map((attribute) =>
attribute.values.map((value) => {
// `options` objects have `value` and `key` properties
return { value, key: attribute.title.text };
})
);
The combinations code remains the same.
Then, we loop over the combinations and create the new variants array
let variants = [];
for (const combination of combinations) {
variants.push({
// use template strings to create the new title
title: `${combination[0].value}/${combination[1].value}`,
slug: "...the value that you want",
// the "combination" variable already has the structure that you want for "options"
options: combination
});
}
The final code will look something like this:
addVariant: function () {
const parts = this.form.attributes.map((attribute) =>
attribute.values.map((value) => {
return { value, key: attribute.title.text };
})
);
const combinations = parts.reduce((a, b) =>
a.reduce((r, v) => r.concat(b.map((w) => [].concat(v, w))), [])
);
let variants = [];
for (const combination of combinations) {
variants.push({
title: `${combination[0].value}/${combination[1].value}`,
slug: "...the value that you want",
options: combination,
});
}
this.variants = variants;
}
Related
I have a function that map and create a new array from a given array. After I map the array to have a key: "value", but the map function return me the "key": "value".
How can I get or map the key not in a string format ?
let categories_name = [{ name: "test", prov_id: "f34f43"}, { name : "test1", prov_id: "233edd3"}]
.map(v => v.prov_id)
.filter((item, index, arr) => arr.indexOf(item) === index);
the result is this
["f34f43","233edd3"]
now I want to add a key (name) for each value and convert in a object
let newArray = categories_name.map(value => ({name: value}));
this is the result :
[ { "name": "f34f43" }, { "name": "233edd3" }]
but I need like that, with key not like a string.
[ { name: "f34f43" }, { name: "233edd3" }]
In a JavaScript object, all keys are strings. So the followings are exactly the same/identical:
{ "key": "value" }
{ key: "value" }
// Hence your example is identical as well:
{ "name": "f34f43" }
{ name: "f34f43" }
When you run the code below, you will see that even your original input has object properties in the form "key": "value" when printed:
let categories_name = [{ name: "test", prov_id: "f34f43"},{ name : "test1", prov_id: "233edd3"}]
console.log('source:', categories_name)
let ids = categories_name.map(v => v.prov_id)
.filter((item, index, arr) => arr.indexOf(item) === index);
console.log('ids:', ids)
let newArray = categories_name.map(value => ({name: value}));
console.log('newArray:', newArray)
That's just the standard JSON representation, what you get if you used JSON.stringify.
If you really really need the string representation to look like ES5 syntax, see my answer to the above linked SO question. Per that answer, below I use JSON5.stringify from the JSON5 library, which has a compatible interface to the built-in JSON object:
// if node, import 'json5' here, as opposed to
// the HTML script tag this example relies on
let categories_name = [{ name: "test", prov_id: "f34f43"},{ name : "test1", prov_id: "233edd3"}]
console.log('source:', JSON5.stringify(categories_name))
let ids = categories_name.map(v => v.prov_id)
.filter((item, index, arr) => arr.indexOf(item) === index);
console.log('ids:', JSON5.stringify(ids))
let newArray = categories_name.map(value => ({name: value}));
console.log('newArray:', JSON5.stringify(newArray))
<script src="https://unpkg.com/json5#^2.0.0/dist/index.min.js"></script>
I wanted to filter out the data. I wanted to check if data on data1 is found on data2 and to check if it has errorMessages. Please check my code below. Is there a better way to do it?
data1
[
{
"ids": "0111",
},
{
"ids": "0222",
},
{
"ids": "0333",
}
]
data2
[
{
"id": "0111",
"errorMessages": [
{
"message": ["sample error message 1"]
}
]
},
{
"id": "0333",
"errorMessages": []
}
]
Code
const output= data1.filter(
(element) => element.ids === data2.find((data) => data).id
);
console.log("output", output);
.find((data) => data) doesn't do anything useful - each item in the array is an object, which is truthy, so that'll always return the first element in the array.
If you did want to .find a matching element in the other array - then a better approach would be to make a Set of the IDs found in the other array first (Set lookup is much quicker - O(1) - than .find, which is O(n)).
You also need to implement the logic to check if the errorMessages is empty.
const data1 = [
{
"ids": "0111",
},
{
"ids": "0222",
},
{
"ids": "0333",
}
]
const data2 = [
{
"id": "0111",
"errorMessages": [
{
"message": ["sample error message 1"]
}
]
},
{
"id": "0333",
"errorMessages": []
}
]
const ids = new Set(
data2
.filter(item => item?.errorMessages.length)
.map(item => item.id)
);
const output= data1.filter(
element => ids.has(element.ids)
);
console.log("output", output);
Without Set, but use Object as the map.
const IDKeys = {};
data2.forEach(data => {
if (data.errorMessages.length){
IDKeys[data.id] = true; // means valid
}
})
const filteredArray = data1.filter(data => IDKeys[data.id]);
This would be only O(n) since accessing key on object is O(1)
[
{
"clauseId": 1,
"clauseName": "cover",
"texts": [
{
"textId": 1,
"text": "hello"
}
]
},
{
"clauseId": 3,
"clauseName": "xyz",
"texts": [
{
"textId": 3,
"text": "hello Everyone"
},
{
"textId": 4,
"text": "Some data"
}
]
}
{
"clauseId": 2,
"clauseName": "joining",
"texts": [
{
"textId": 3,
"text": "hello1"
},
{
"textId": 4,
"text": "hello2"
}
]
}
]
If I make a list like
a=[joining,cover]
I want a new list to be formed as
b=[hello1,hello2,hello]
Please note that index of every element matters If i reverse the index the
b =[hello,hello1,hello2]
If
a=[xyz,joining,cover]
b=["hello Everyone","Some data",hello1,hello2,hello]
Similary If I interchange the places in a like [joining,xyz,cover]
b=["hello1","hello2","hello Everyone","Some data",hello]
Please note that the incoming data can have multiple clauseName and multiple texts in it..This is just a demo
You can create a look up table from your original array by using a Map. The map will use each clauseName as the key and point to an array of texts as the value. You can then .flatMap() your clauses array to values at each key in your look up table (ie the Map).
See example below:
const arr=[{clauseId:1,clauseName:"cover",texts:[{textId:1,text:"hello"}]},{clauseId:3,clauseName:"xyz",texts:[{textId:3,text:"hello Everyone"},{textId:4,text:"Some data"}]},{clauseId:2,clauseName:"joining",texts:[{textId:3,text:"hello1"},{textId:4,text:"hello2"}]}];
const clauses = ["joining", "cover"];
const lut = new Map(arr.map(({clauseName, texts}) =>
[clauseName, texts.map(({text}) => text)]
));
const result = clauses.flatMap(key => lut.get(key));
console.log(result);
If you cannot use JS's native .flatMap implementation you can either polyfill it or use lodash's implementation:
const arr = [{clauseId:1,clauseName:"cover",texts:[{textId:1,text:"hello"}]},{clauseId:3,clauseName:"xyz",texts:[{textId:3,text:"hello Everyone"},{textId:4,text:"Some data"}]},{clauseId:2,clauseName:"joining",texts:[{textId:3,text:"hello1"},{textId:4,text:"hello2"}]}];
const clauses = ["joining", "cover"];
const lut = new Map(arr.map(({clauseName, texts}) =>
[clauseName, texts.map(({text}) => text)]
));
const result = _.flatMap(clauses, key => lut.get(key));
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>
I'm using underscore to extract some props into a separate object but the structure is not as I want:
let element = {
foo: 0,
bar: 1,
baz: _.map(
_.filter(element.properties, (prop) =>
_.contains(validationProps, prop.name)), (rule) =>
({ [rule.name]: rule.value }) )
}
.. returns an array of objects for baz:
[ {"required":true} , {"maxLength":242} ]
.. what I need however is:
{ "required":true, "maxLength":242 }
Or use JavaScript's Array.prototype.reduce()
The reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value.
let data = [{
"name": "label",
"value": "Short 2"
},
{
"name": "required",
"value": true
},
{
"name": "maxLength",
"value": 242
}
];
let reformatted = data.reduce((pv, cv) => {
pv[cv.name] = cv.value;
return pv;
}, {});
console.log(reformatted);
What is the Best way to convert,
{
"columns":[
"name",
"color"
],
"values":[
[
"lion",
"yellow"
],
[
"crow",
"black"
]
]
}
into
{
"data":[
{
"name":"lion",
"color":"yellow"
},
{
"name":"crow",
"color":"black"
}
]
}
Rather than loop, Is there any function available ? Can I achieved it through something like extend() ?
You could use Object.assign with spread syntax ... for the parts.
var object = { columns: ["name", "color"], values: [["lion", "yellow"], ["crow", "black"]] },
result = { data: object.values.map(v => Object.assign(...object.columns.map((c, i) => ({[c]: v[i]})))) };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do do it with combination of map and reduce. Reducing original columns array will allow to code to work with any number of columns, it will pick up corresponding value by index:
const result = {data: data.values.map(el => {
return data.columns.reduce((prev, curr, index) => {
prev[curr] = el[index]
return prev
}, {})
})}
Check the demo below.
const data = {
"columns":[
"name",
"color"
],
"values":[
[
"lion",
"yellow"
],
[
"crow",
"black"
]
]
}
const result = {data: data.values.map(el => {
return data.columns.reduce((prev, curr, index) => {
prev[curr] = el[index]
return prev
}, {})
})}
console.log(result)
You can get vales of .columns array using destructuring assignment, spread element; for..of loop to assign computed properties of .columns array as strings as properties of objects at data array by iterating .values property of original object, assign value of each array as value of created object
let obj = {
"columns":[
"name",
"color"
],
"values":[
[
"lion",
"yellow"
],
[
"crow",
"black"
]
]
}
let [res, key, value] = [{data:Array()}, ...obj.columns];
for (let [a, b] of [...obj.values]) res.data.push({[key]:a, [value]:b});
console.log(res);