What could be an optimized way of deleting keys from an object? - javascript

I had a use case to remove multiple keys from an JSON object or an array of objects.Below is my code.If anyone has more optimized approach for this then suggest.
exports.removeAttributes = function (arrayOfAttributesToRemove, object, callback) {
let checkForRemoveOrRetain = true;
removeORRetain(arrayOfAttributesToRemove, object, checkForRemoveOrRetain, (err, object) => {
if (err) {
callback(err);
} else {
callback( object);
}
});
};
exports.retainAttributes = function (arrayOfAttributesToRetain, object, callback) {
let checkForRemoveOrRetain = false;
removeORRetain(arrayOfAttributesToRemove, object, checkForRemoveOrRetain, (err, object) => {
if (err) {
callback(err);
} else {
callback(object);
}
});
}
let removeORRetain = function (arrayOfAttributesToRemove, object, checkForRemoveOrRetain, callback) {
if (Array.isArray(object)) {
for (let i = 0; i < object.length; i++) {
for (let key in object[i]) {
if (arrayOfAttributesToRemove.includes(key) === checkForRemoveOrRetain) {
delete object[i][key];
}
}
}
callback(object);
} else {
for (let key in object) {
if (arrayOfAttributesToRemove.includes(key) === checkForRemoveOrRetain) {
delete object[key];
}
}
callback( object);
}
};

You can do it with lodash very easily.
const _ = require('lodash');
exports.removeAttributes = function (arrayOfAttributesToRemove, object, callback) {
// check for errors etc. Then
// Create an array of objects if not already.
const arr = _.concat([], object);
// call callback with new arr with omitted properties using _.map and _.omit
callback(null, _.map(arr, e => _.omit(e, arrayOfAttributesToRemove)));
};
If you want to retain properties then use _.pick

Related

A way to convert an object with keys of . seperated strings into a JSON object

I'm trying to figure out a way to turn and object like this :
{ "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" }
into JSON like this:
{ "test": { "subtest": { "pass": "test passed", "fail": "test failed" }}}
sometimes there may be duplicate keys, as above perhaps there would be another entry like "test.subtest.pass.mark"
I have tried using the following method and it works but it's incredibly ugly:
convertToJSONFormat() {
const objectToTranslate = require('<linkToFile>');
const resultMap = this.objectMap(objectToTranslate, (item: string) => item.split('.'));
let newMap:any = {};
for (const [key,value] of Object.entries(resultMap)) {
let previousValue = null;
// #ts-ignore
for (const item of value) {
// #ts-ignore
if (value.length === 1) {
if(!newMap.hasOwnProperty(item)) {
newMap[item] = key
} // #ts-ignore
} else if (item === value[value.length - 1]) {
if(typeof previousValue[item] === 'string' ) {
const newKey = previousValue[item].toLowerCase().replace(/\s/g, '');;
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][newKey] = newValue;
previousValue[item][item] = key;
} else {
previousValue[item] = key;
}
} else if (previousValue === null) {
if (!newMap.hasOwnProperty(item)) {
newMap[item] = {};
}
previousValue = newMap[item];
} else {
if (!previousValue.hasOwnProperty(item)) {
previousValue[item] = {}
previousValue = previousValue[item];
} else if (typeof previousValue[item] === 'string') {
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][item] = newValue;
} else {
previousValue = previousValue[item];
}
}
}
}
return newMap;
}
We can utilize recursion to make the code a little less verbose:
function convertToJSONFormat(objectToTranslate) {
// create root object for the conversion result
const result = {};
// iterate each key-value pair on the object to be converted
Object
.entries(objectToTranslate)
.forEach(([path, value]) => {
// utilize a recursive function to write the value into the result object
addArrayPathToObject(result, path.split("."), value);
});
return result;
}
function addArrayPathToObject(root, parts, value) {
const p = parts.shift();
// base-case: We attach the value if we reach the last path fragment
if (parts.length == 0) {
root[p] = value
return;
}
// general case: check if root[p] exists, otherwise create it and set as new root.
if(!root[p]) root[p] = {};
addArrayPathToObject(root[p], parts, value)
}
This function utilizes the fact that objects are pass-by-reference to recursively traverse through the object starting at its root until setting the desired value.
You can add error-handling and other such concerns as necessary for your use.
#Meggan Naude, toJson function copies json object to reference obj for provided keys and value.
const p = { "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" };
const result = {} ;
const toJson = (obj, keys, value) => {
if (keys?.length === 1) {
obj[keys[0]] = value;
return obj
} else {
const k = keys.splice(0, 1)
if (k in obj) {
toJson(obj[k], keys, value)
} else {
obj[k] = {};
toJson(obj[k], keys, value)
}
return obj
}
}
Object.keys(p).forEach(key => toJson(result, key.split('.'), p[key]))
console.log(result);

Uexpected Function Behavior. JS Classes

I've created HTML builder (finally!)
It just has methods: create div, span, p, br.
As you can see in console.logs it has nesting and chaining behavior.
And for detecting nesting and chaining I have item instanceof Class
But it doesn't show me correct return when I have nesting condition.
Need help to find the mistake and get output in first console.log =
<div><p>Hello</p><p>World</p></div>
class Templater {
constructor() {
this.arr = [];
this.nesting = false;
}
transform(tags) {
return tags.join("");
}
div(...tags) {
tags.forEach(item => {
this.nesting = (item instanceof Templater);
});
this.arr.push(`<div>${this.transform(tags)}</div>`)
return this;
}
span(...tags) {
tags.forEach(item => {
this.nesting = (item instanceof Templater);
});
this.arr.push(`<span>${this.transform(tags)}</span>`);
return this
}
br(argument) {
tags.forEach(item => {
this.nesting = (item instanceof Templater);
});
if (argument) {
throw new Error('Nested content is not allowed');
} else {
this.arr.push(`<br>`);
return this;
}
}
p(...tags) {
tags.forEach(item => {
this.nesting = (item instanceof Templater);
});
this.arr.push(`<p>${this.transform(tags)}</p>`);
return this
}
toString() {
if (this.nesting) {
this.nesting = false;
let qwe = [...this.arr];
this.arr = [];
return qwe[qwe.length-1];
} else {
let qwe = [...this.arr];
this.arr = [];
return qwe.join('');
}
}
}
const template = new Templater();
console.log(template.div(
template.p('Hello'),
template.p('World')
).toString());
console.log(template.div().toString());
console.log(template.div('Hello').p('fix').toString());
Worked for me with :
console.log(template.div(
template.p('Hello').p('World').toString()
).toString());

How to handle different optional callbacks in the same function?

Node.js
I have a function that works with different callbacks. Now I'm wondering if there is a better solution handling different callback functions.
My situation:
function prepare(type, callback){
let obj = {}; //in real is this an external database
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
if(type === 'createNewUser'){
callback(obj); //callback for function createNewUser(obj);
}
if(type === 'addToExistingUser'){
callback(obj); //callback for function addToExistingUser(obj);
}
}
My callback functions are:
createNewUser(obj){
//create new user with obj;
}
addToExistingUser(obj){
//add obj to existing user
}
2 ways to use prepare();
prepare('createNewUser', createNewUser);
prepare('addToExistingUser', addToExistingUser);
What is the best practice for this case? I would like to write good code.
How about this?
Basically it's called bracket notation you create a map that has REFERENCE to the function, you can call that function with the desired params.
var callbacks = {
createNewUser : createNewUserCallback,
addToExistingUser: addToExistingUserCallback
}
function prepare(type){
let obj = {};
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
callbacks[type](obj)
}
function addToExistingUserCallback(obj) {
// Do stuff
}
function createNewUserCallback(obj) {
// Do stuff
}
OR
var callbacks = {
createNewUser : (obj) => { // function code goes here.} ,
addToExistingUser: (obj) => {// function code goes here.}
}
function prepare(type){
let obj = {};
for(let i=1; i<= 10; i++){
obj['progress_'+i] = 0;
}
callbacks[type](obj)
}
To be fair option 1 is more readable.
You can try below example and add multiple callbacks in the same function according to your requirement -
function prepare(type, callback, secondcallback) {
let obj = {};
for (let i = 1; i <= 10; i++) {
obj['progress_' + i] = 0;
}
if (type === 'createNewUser') {
callback(obj); //callback for function createNewUser(obj);
}
if (type === 'addToExistingUser') {
secondcallback(obj); //callback for function addToExistingUser(obj);
}
}
prepare('createNewUser', function (data) {
console.log('First Callback data:', data);
}, function (data) {
console.log('Second Callback data:' + data);
})
prepare('addToExistingUser', function (data) {
console.log('First Callback data:', data);
}, function (data) {
console.log('Second Callback data:' + data);
})
If type is a string containing the name of the function to call, you can use window[functionName]()
function foo()
{
console.log("called foo");
}
function bar()
{
console.log("called bar");
}
function baz(str)
{
window[str]();
}
baz('foo');
baz('bar');
Or you can create your own object and store the functions in it :
const FunctionObject = {}
FunctionObject.foo = function ()
{
console.log("called foo");
};
FunctionObject.bar = function ()
{
console.log("called bar");
};
function baz(str)
{
FunctionObject[str]();
}
baz('foo');
baz('bar');
replace
if(type === 'createNewUser'){
callback(obj); //callback for function createNewUser(obj);
}
if(type === 'addToExistingUser'){
callback(obj); //callback for function addToExistingUser(obj);
}
With
fnc = window[callback];
if( fnc && typeof fnc === "function" ) { //make sure it exists and it is a function
fnc(); //execute it
}
I am not sure why you need type when you know what method to call or clarify if the type is not hardcoded, you simply can use prototypes as per your defined requirements.
function Prepare(obj) {
this.obj = obj;
}
Prepare.prototype = {
createNewUser(obj = this.obj) {
console.log('existing', obj);
},
addToExistingUser(obj = this.obj) {
console.log('new', obj)
}
}
let prep = new Prepare({a:1});
prep.createNewUser();
//or
prep.createNewUser({c: 3});
prep.addToExistingUser({b:2})
If the type is not hardcoded
function Prepare(type, obj) {
this.obj = obj;
this.callback = this[type];
}
Prepare.prototype = {
createNewUser(obj = this.obj) {
console.log('existing', obj);
},
addToExistingUser(obj = this.obj) {
console.log('new', obj)
}
}
//if type is not hardcoded
let prep = new Prepare('createNewUser');
prep.callback({d: 4});

Javascript loop not returning true, when String === String

When looping through an array to find if the array contains a word that I am looking for, the loop always returns 'false' when if I console.log out the what is being compared I can clearly see that the word I am looking for (collectionNameLookingFor) is in the array (collectionNameArray) so it should return true.
function checkCollectionNames(arrayOfCollections, collectionName) {
for (let i = 0; i < arrayofCollections.length; i++) {
if (arrayOfCollections[i] === collectionName) {
return true;
}
}
return false;
}
function saveContentToDb(req, res) {
const db = getDb();
const pageDetails = req.body;
let saveType;
db.db(pageDetails.databaseName).listCollections().toArray((error, collections) => {
if (error) {
throw error;
} else {
collections.map(collection => (collection.name)).forEach(collectionNameArray => {
const collectionNameLookingFor = req.body.page;
const check = checkCollectionNames(collectionNameArray, collectionNameLookingFor);
console.log('===========Looking to see if it is true or false==========');
console.log(check);
console.log(`Name of collection in Database: ${collectionNameArray} ::: ${collectionNameLookingFor}`);
console.log('==========================================================');
if (check === true) {
saveType = 'updated';
console.log(`saveType = ${saveType}`);
} else {
saveType = 'created';
console.log(`saveType = ${saveType}`);
}
});
}
});
}
You might need to check against collectionName, because that is the parameter you hand over, beside arrayOfCollections, instead of the array itself.
function checkCollectionNames(arrayOfCollections, collectionName) {
for (let i = 0; i < arrayOfCollections.length; i++) {
if (arrayOfCollections[i] === collectionName) {
return true;
}
}
return false;
}
Short Version:
function checkCollectionNames(arrayOfCollections, collectionName) {
return arrayOfCollections.includes(collectionName);
}

Chrome storage custom serialization

For example. I have something like this:
class Qwe {
constructor(q, w, e) {
this.q = q;
this.w = w;
this.e = e;
}
toJSON() {
return {q: this.q, w: this.w};
}
}
And i want to store object of this class in chrome.storage, but it would be something like this:
{q:q, w:w, e:e}
And i wonder if there is any way to custom serialization like here with toJSON, or if not how you would resolve this problem.
Only way to do this is implement your own serialization (and perhaps deserialization) and call it on main object before store in chrome.storage:
...
let qwe = new Qwe("q", "w", "e");
...
let item = {qwe: qwe.serialize()}; // in this case toJSON()
chrome.storage.local.set(item);
...
About implementation of serialization:
I have created something like this for main object:
let mainObject = {
//...
//a lot of objects literals, arrays, and our classes (objects)
//...
}
function serialize(sourceObject, object) {
if(sourceObject instanceof Array) {
if(object instanceof Array) {
let id = sourceObject.push([]) - 1;
object.forEach(item => {
serialize(sourceObject[id], item);
});
} else if(object instanceof Object) {
if(object.serializable) {
sourceObject.push(object.serialize());
} else {
let id = sourceObject.push({}) - 1;
serialize(sourceObject[id], object);
}
} else {
sourceObject.push(object);
}
} else {
for(let property in object) {
if(object.hasOwnProperty(property)) {
if(object[property] instanceof Array) {
sourceObject[property] = [];
object[property].forEach(item => {
serialize(sourceObject[property], item);
});
} else if(object[property] instanceof Object) {
if(object[property].serializable) {
sourceObject[property] = object[property].serialize();
} else {
sourceObject[property] = {};
serialize(sourceObject[property], object[property]);
}
} else {
sourceObject[property] = object[property];
}
}
}
}
}
If object has property serializable then i know that it implements serialize() otherwise i go deeper. I don't know if this is optimal solution ;).
Usage:
let o = {
mainObject: {}
};
serialize(o.mainObject, mainObject);
chrome.storage.local.set(o);

Categories