Generate vue components from a nested object - javascript

Is there any good way to use a nested object to generate vue components?
I have a deeply nested object that looks like this:
"api": {
"v1": {
"groups": {
"create": true,
"get": true,
"item": {
"get": true,
"destroy": false
}
}
}
}
I want to generate a form which has checkboxes for each of the values of the object.
I'm having trouble binding the values of the object to the v-models in a Vue checkbox
I've tried making a list of lookup keys like ["api.v1.groups.create", "api.v1.groups.get"]
then using a function like the following to get the entries:
getPerm (p) {
return p.split('.').reduce(
(xs, x) => (xs && xs[x]) ? xs[x] : null,
this.role.permissions)
}
However, this does not work because it gives me the boolean and not the reference.

Check out my:
Code sandbox to flatten dict and create a list from a nested entry
( I forked it from a basic Vue template so it still has some references to 'chart' in there)
Once you have flattened the dictionary you use them to generate vue components using v-for as you normally would!
Below is the processing I used to flatten the dictionary if you would like to check that here on site:
// Define out nested object
var nested = {
"api": {
"v1": {
"groups": {
"create": true,
"get": true,
"item": {
"get": true,
"destroy": false
}
}
}
}
}
// function for checking if we are at the bottom of the Object
const isObj = o => o?.constructor === Object;
// I had to use a class to store the output in the different recursive scopes
class NestedProcessor {
// Constructur starts the function and returns the dictionary as flat
constructor(leveled_dict) {
this.output = {}
this.process_dict_recursive(leveled_dict)
return this.ouput
}
process_dict_recursive(leveled_dict, keyList = [], depth = 0) {
if (isObj(leveled_dict)) { // check if we have hit the bottom
keyList.push('')
depth++
for (let key in leveled_dict) {
keyList[depth - 1] = key
this.process_dict_recursive(leveled_dict[key], keyList, depth) // call the function recursively at our new depth
}
}
else {
// Create our lookup keys
let path = ''
keyList.forEach((v) => {
path += v
path += '.'
})
path = path.slice(0, -1) // Remove the last '.'
this.output[path] = leveled_dict
}
}
}
console.log(new NestedProcessor(nested))
//{
// "output": {
// "api.v1.groups.create": true,
// "api.v1.groups.get": true,
// "api.v1.groups.item.get": true,
// "api.v1.groups.item.destroy": false
// }
//}
Notes:
I used a class, because I couldn't figure out how to handle variable scope within the recursion
I needed a way to check if we were at the bottom so grabbed a function from SO to check for that.

Related

How to get first properties of each object in an array of objects?

const combinations = [{rolledOnes: true, scoredOnes:false},
{rolledTwos: true, scoredTwos:false}];
I am fairly new to Javascript. So, my actual array is larger than this. I want to set rolledOnes and rolledTwos to false, without affecting scoredOnes and scoredTwos. Some sort of loop or nice method would be nice?
I tried an array of arrays and can get it to function the way i want, but it is not clear compared to objects.
We can using Array.forEach() combined with Object.keys() to do it
let combinations = [{rolledOnes: true, scoredOnes:false},
{rolledTwos: true, scoredTwos:false}];
combinations.forEach(e => {
let k = Object.keys(e)[0]
e[k] = false
})
console.log(combinations)
While object key order is deterministic in ES2015+, it's better not to rely on object key order in your logic.
A safer approach might be to store the targeted keys in an array, and then use that array as a filter while iterating over your objects. This approach also better describes your actual intent and will work even in cases where the object key orders are not what you showed in the question details. For example:
const combinations = [
{rolledOnes: true, scoredOnes: false},
{rolledTwos: true, scoredTwos: false},
];
const falseKeys = ['rolledOnes', 'rolledTwos'];
for (const obj of combinations) {
for (const key of falseKeys) {
if (key in obj) obj[key] = false;
}
}
console.log(combinations); // [ { rolledOnes: false, scoredOnes: false }, { rolledTwos: false, scoredTwos: false } ]
Use forEach() function to loop through object
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
And keys() to get property names
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
const combinations = [
{ rolledOnes: true, scoredOnes: false },
{ rolledTwos: true, scoredTwos: false }
];
combinations.forEach(combination => {
let property = Object.keys(combination)[0];
combination[property] = false;
return combination;
})
const combinations = [{
rolledOnes: true,
scoredOnes: false
},
{
rolledTwos: true,
scoredTwos: false
},
{
rolledThrees: true,
scoredThrees: false
},
];
combinations.forEach(comb => {
Object.keys(comb).map(key => {
if (key.startsWith('rolled')) {
comb[key] = false;
}
})
})
console.log(combinations);

Iterate deeply nested object with unknown level and add remove key/value based on user provided conditions

Could anyone please guide me how to achieve the below challenge which I am facing?
I have thousands of mock API request response JSON files. They are deeply nested, and they all are structured differently. I need to add/update/delete entry at the specfic location where the condition match which will be provided by user. I am not sure how to approach this problem? I have tried doing something like below. I am asking user for path for where to start looking. But this will increase time as user has to look for path in all file and pass that info to api. below code work upto 2 level only. need to search full tree where all user provides conditions matches, and at that place, I need to add/update/delete data. I took condition as an array of objects.
Draft Code
const _ = require("lodash");
const file = "./sample.json";
const actions = ["add", "delete", "update"];
const consumer = (file, key, where, data, action) => {
try {
const act = action.toLowerCase();
if(!actions.includes(act) throw new Error("invalid action provided");
if(_.isArray(where) && _.every(where, _.isObject())) throw new Error("no where clause condition provided");
let content = require(file);
let typeKeyContent = null;
let keyContent = _.get(content, key);
if(!keyContent) throw new Error("invalid key");
if(_.isArray(keyContent)) {
typeKeyContent = "array"
} else if (_.isObject(keyContent)) {
typeKeyContent = "object"
}
switch (act) {
case "add":
if (typeKeyContent === "array") {
// array logic
for (let i = 0; i < keyContent.length; i++) {
const result = where.every(element => {
for (let key in element) {
return keyContent[key] && element[key] === keyContent[key];
}
});
if (!result) {
console.log("attributes matching -> ", result);
return;
}
keyContent[i] = {...keyContent[i], ...data }
}
let newcontent = _.set(content, key, keyContent);
console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
return;
}
const result = where.every(element => {
for (let key in element) {
return keyContent[key] && element[key] === keyContent[key];
}
});
if (!result) {
console.log("attributes matching -> ", result);
return;
}
keyContent = { ...keyContent, ...data };
let newcontent = _.set(content, key, keyContent);
console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
// TODO :: store back in json file
break;
default:
console.log("reached default case");
return;
}
} catch(err) {
console.log("ERROR :: CONSUMER ::", error);
}
}
// AND based condition only
const conditions = [
{ name: "Essential Large" },
{ selected: true }
];
const newdata = { description: "our best service" } // wants to add new prop
consumer(file, "selected_items.essential", conditions, newdata, "add");
sample json
{
"status": 200,
"request": {},
"response": {
"ffs": false,
"customer": {
"customer_id": 1544248,
"z_cx_id": 123456
},
"selected_items": {
"essential": [
{
"id": 4122652,
"name": "Essential Large",
"selected": true,
"description": "our best service" // will be added
},
{
"id": 4122653,
"name": "Essential Large",
"selected": true,
"description": "our best service" // will be added
}
]
},
"service_partner": {
"id": 3486,
"name": "Some String",
"street": "1234 King St."
},
"subject": "Project",
"description": "Issue: (copy/paste service request details here Required"
}
}
So you want to go through every key of a nested object right?
function forEvery(object,fn){
//obj is the object, fn is the function
//this function should go through each item in an object loaded from JSON string
//fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
var arr=[]
function recurse(obj,map,depth){
Object.keys(obj).forEach((a,i)=>{
fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
map.push(a); arr.push(map)
recurse(obj[a],[...map],depth+1)
}
})
}
recurse(object,[],1)
}
//usage would be like:
//let customerCondition=/*some logic here*/
//let testObj=JSON.parse( (require('fs')).readFileSync('dirToSomeFile.json') )
forEvery(testObj,customerCondition)
Here's a live example
let testObj={"status":200,"request":{},"response":{"ffs":false,"customer":{"customer_id":1544248,"z_cx_id":123456},"selected_items":{"essential":[{"id":4122652,"name":"Essential Large","selected":true},{"id":4122653,"name":"Essential Medium","selected":false}]},"service_partner":{"id":3486,"name":"Some String","street":"1234 King St."},"subject":"Project","description":"Issue: (copy/paste service request details here Required"}}
function forEvery(object,fn){
//obj is the object, fn is the function
//this function should go through each item in an object loaded from JSON string
//fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
var arr=[]
function recurse(obj,map,depth){
Object.keys(obj).forEach((a,i)=>{
fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
map.push(a); arr.push(map)
recurse(obj[a],[...map],depth+1)
}
})
}
recurse(object,[],1)
}
//example usage
let userQuery=[{ name: "Essential Large" },{ selected: true }]; //the user query in the format you gave
let userCondition={} //assuming each key across userQuery is unique, I set a model object for comparisons later on
userQuery.forEach(obj=>{ //I fill the model object :D
Object.keys(obj).forEach(key=>{
userCondition[key]=obj[key]
})
})
let testFn=(elem,parent,key,depth)=>{
//I use comparisons with the model object
let condition=typeof elem!="object"?false:
Object.keys(userCondition)
.every(item=>userCondition[item]==elem[item])
//true if matches user condition(meaning elem must be an object), false otherwise
if(condition){
console.log(parent[key],"will now be deleted")
delete(parent[key]) //deletion example(if user conditions match)
}
}
forEvery(testObj,testFn)
console.log("and the changed object looks like",testObj)

How to parse through large JSONL data Node js

I am trying to read through a large JSONL, maybe couple hundreds up to thousands or possibly million line, below is sample of of the data.
{"id":"gid://shopify/Product/1921569226808"}
{"id":"gid://shopify/ProductVariant/19435458986040","__parentId":"gid://shopify/Product/1921569226808"}
{"id":"gid://shopify/Product/1921569259576"}
{"id":"gid://shopify/ProductVariant/19435459018808","__parentId":"gid://shopify/Product/1921569259576"}
{"id":"gid://shopify/Product/1921569292344"}
{"id":"gid://shopify/ProductVariant/19435459051576","__parentId":"gid://shopify/Product/1921569292344"}
{"id":"gid://shopify/Product/1921569325112"}
{"id":"gid://shopify/ProductVariant/19435459084344","__parentId":"gid://shopify/Product/1921569325112"}
{"id":"gid://shopify/Product/1921569357880"}
{"id":"gid://shopify/ProductVariant/19435459117112","__parentId":"gid://shopify/Product/1921569357880"}
{"id":"gid://shopify/ProductVariant/19435458986123","__parentId":"gid://shopify/Product/1921569226808"}
So each line is json object, either its a Product, or a Product Child identified by __parentId, given that the data may contain thousands of lines, what's the best way to read through it and return a regular JSON object, like this.
{
"id": "gid://shopify/Product/1921569226808",
"childrens": {
{"id":"gid://shopify//ProductImage//20771195224224","__parentId":"gid:////shopify//Product//1921569226808"},
{"id":"gid:////shopify//ProductImage//20771195344224","__parentId":"gid:////shopify//Product//1921569226808"}
{"id":"gid:////shopify//ProductImage//20771329344224","__parentId":"gid:////shopify//Product//1921569226808"}
}
}
The data is coming back from Shopify and they advice to:
Because nested connections are no longer nested in the response data
structure, the results contain the __parentId field, which is a
reference to an object's parent. This field doesn’t exist in the API
schema, so you can't explicitly query it. It's included automatically
in bulk operation result.
Read the JSONL file in reverse Reading the JSONL file in reverse makes
it easier to group child nodes and avoids missing any that appear
after the parent node. For example, while collecting variants, there
won't be more variants further up the file when you come to the
product that the variants belong to. After you download the JSONL
file, read it in reverse, and then parse it so that any child nodes
are tracked before the parent node is discovered.
You can look for look here to read more about all of thisenter link description here.
Consider using streams so that you don't have to load the entire file in memory.
You can use readline (a native module) to process each line individually.
I took the line processing part from #terrymorse https://stackoverflow.com/a/65484413/14793527
const readline = require('readline');
const fs = require('fs');
let res = {};
function processLine(line) {
const {id, __parentId} = line;
// if there is no `__parentId`, this is a parent
if (typeof __parentId === 'undefined') {
res[line.id] = {
id,
childrens: []
};
return res;
}
// this is a child, create its parent if necessary
if (typeof res[__parentId] === 'undefined') {
res[__parentId] = {
id: __parentId,
childrens: []
}
}
// add child to parent's children
res[__parentId].childrens.push(line);
return res;
}
const readInterface = readline.createInterface({
input: fs.createReadStream('large.jsonl'),
output: process.stdout,
console: false
});
readInterface.on('line', processLine);
readInterface.on('close', function() {
const resultArray = Object.values(res);
console.log(resultArray);
});
Here's a technique that:
forms an object with properties of the parent ids
converts that object to an array
(input lines converted to an array for simplicity)
const lines = [
{ "id": "gid://shopify/Product/1921569226808" },
{ "id": "gid://shopify/ProductVariant/19435458986040", "__parentId": "gid://shopify/Product/1921569226808" },
{ "id": "gid://shopify/Product/1921569259576" },
{ "id": "gid://shopify/ProductVariant/19435459018808", "__parentId": "gid://shopify/Product/1921569259576" },
{ "id": "gid://shopify/Product/1921569292344" },
{ "id": "gid://shopify/ProductVariant/19435459051576", "__parentId": "gid://shopify/Product/1921569292344" },
{ "id": "gid://shopify/Product/1921569325112" },
{ "id": "gid://shopify/ProductVariant/19435459084344", "__parentId": "gid://shopify/Product/1921569325112" },
{ "id": "gid://shopify/Product/1921569357880" },
{ "id": "gid://shopify/ProductVariant/19435459117112", "__parentId": "gid://shopify/Product/1921569357880" },
{ "id": "gid://shopify/ProductVariant/19435458986123", "__parentId": "gid://shopify/Product/1921569226808" }
];
// form object keyed to parent ids
const result = lines.reduce((res, line) => {
const {id, __parentId} = line;
// if there is no `__parentId`, this is a parent
if (typeof __parentId === 'undefined') {
res[id] = {
id,
childrens: []
};
return res;
}
// this is a child, create its parent if necessary
if (typeof res[__parentId] === 'undefined') {
res[__parentId] = {
id: __parentId,
childrens: []
}
}
// add child to parent's children
res[__parentId].childrens.push(line);
return res;
}, {});
// convert object to array
const resultArray = Object.values(result);
const pre = document.querySelector('pre');
pre.innerText = 'resultArray: ' + JSON.stringify(resultArray, null, 2);
<pre></pre>

Vue JS - Reactivity $set property of property in an object array

My Vue.js version is v1.0.28. I was wondering how I could use Vue.$set to set a deep property of an object array. For example, I have:
this.pairs = [
{
"left_media": {
"name": "A",
"show": false
},
"right_media": {
"name": "B",
"show": false
}
},
{
"left_media": {
"name": "B",
"show": false
},
"right_media": {
"name": "A",
"show": false
}
}
]
I need to loop through this.pairs array and update the show property of each media name == A to true.
I can seem to update them easily via a for loop, but the view won't change. I've done some research and seems like Vue.$set can help fix this, but I can't seem to get it work in this case.
How do I use $set to do: this.pairs[i].left_media.show = false;?
EDIT
AJAX call to update show_comment
toggleComment: function(media, which_media) {
this.$http.post('/api/project-media/update.json', {
id: media.id,
show_comment: !media.show_comment
})
.then(function (response) {
if (response.body.success) {
// update show_comment status for this media in all pairs\
for (let pair of this.pairs) {
for (let key of Object.keys(pair)) {
if (key == 'project_media_left' || key == 'project_media_right') {
if (pair[key].id == media.id) {
this.$set(pair[key], 'show_comment', !media.show_comment);
}
}
}
}
}
}, function (response) {
});
}
If your target environment supports Object.values or you are using babel or something to compile, then its just
for (let pair of this.pairs)
for (let v of Object.values(pair))
if ('A' == v.name) v.show = true
If not then
for (let pair of this.pairs)
for (let key of Object.keys(pair))
if (pair[key].name == 'A')
this.$set(pair[key], 'show', true)
Example.
But to be honest,
for (let pair of this.pairs)
for (let key of Object.keys(pair))
if (pair[key].name == prop)
pair[key].show = true
also works for me, so I wonder if there is something else going on with your issue.

Using constants as indices for JavaScript associative arrays

I'm looking to create an associative array in JavaScript, but use constants defined as part of the class as indices.
The reason I want this is so that users of the class can use the constants (which define events) to trigger actions.
Some code to illustrate:
STATE_NORMAL = 0;
STATE_NEW_TASK_ADDED = 0;
this.curr_state = STATE_NEW_TASK_ADDED;
this.state_machine = {
/* Prototype:
STATE_NAME: {
EVENT_NAME: {
"next_state": new_state_name,
"action": func
}
}
*/
STATE_NEW_TASK_ADDED : { // I'd like this to be a constant
this.EVENT_NEW_TASK_ADDED_AJAX : {
"next_state": STATE_NEW_TASK_ADDED,
"action" : function() {console.log("new task added");},
}
}
}
// Public data members.
// These define the various events that can happen.
this.EVENT_NEW_TASK_ADDED_AJAX = 0;
this.EVENT_NEW_TASK_ADDED_AJAX = 1;
I'm having trouble getting this to work. I'm not too great with JavaScript, but it looks like no matter what I do, the array gets defined with strings and not constants. Is there a way to force the array to use the constants?
In ECMAScript 6 you can use computed values for object keys:
var CONSTANT_A = 0, CONSTANT_B = 1
var state_machine = {
[CONSTANT_A]: function () {
return 'a'
},
[CONSTANT_B]: function () {
return 'b'
}
};
console.log(state_machine)
This does not work in Internet Explorer 11 nor in Safari browsers:
https://kangax.github.io/compat-table/es6/#test-object_literal_extensions_computed_properties
See Kristian's answer re: ECMAScript 6/modern JavaScript, which has new syntax to make this possible.
The below is my original answer, from the pre-modern age.
The problem here, actually, is that you can't use a value for the key part when you're defining an object literally.
That is to say, this uses the constant values as expected:
var CONSTANT_A = 0, CONSTANT_B = 1;
var state_machine = {};
state_machine[CONSTANT_A] = "A";
state_machine[CONSTANT_B] = "B";
console.log(state_machine[0]); // => A
console.log(state_machine[1]); // => B
But this won't work as expected, instead using the string CONSTANT_A as key:
var CONSTANT_A = 0, CONSTANT_B = 1;
var state_machine = {
CONSTANT_A: "A",
CONSTANT_B: "B",
};
console.log(state_machine[0]); // => undefined
console.log(state_machine["CONSTANT_A"]); // => A
console.log(state_machine.CONSTANT_A); // => A
JavaScript has a shorthand to define object literals where you can omit the double-quotes around keys. Expressions can't be used, so CONSTANT_A won't be evaluated.
Let's say you have the following constants:
const COMPANIES = "companies";
const BRANCHES = "branches";
const QUEUES = "queues";
const LOGOUT = "logout";
If you declare the dictionary this way:
var itemsToState = {
COMPANIES: true,
BRANCHES: false,
QUEUES: false,
LOGOUT: false,
}
// You will get:
// { COMPANIES: true, BRANCHES: false, QUEUES: false, LOGOUT: false }
Note the keys are uppercase ^ because it is not using the constant's value.
If you want to use the constant's value as key, you need to do this:
var itemsToState = {
[COMPANIES]: true,
[BRANCHES]: false,
[QUEUES]: false,
[LOGOUT]: false,
}
// You will get:
// { companies: true, branches: false, queues: false, logout: false }
Note the keys are lowercase ^ because it is using the constant's value.

Categories

Resources