Single function for saving into localStorage? - javascript

How can we save and retain localStorage objects rather than creating multiple functions ? The first localStorage object get replaced with the new save. So to avoid that I have created a new function called saveLocalStorageDataTwo which is working. But how can we avoid creating multiple functions for saving data into the localStorage ? Is there any way ? Could some please advise ?
/* localStorage.js */
let localStorageData = {};
let localStorageDataTwo = {};
function saveLocalStorageData ({autoId, quoteId, taskId }) {
localStorageData = {
autoId: autoId,
quoteId: quoteId,
taskId: taskId,
}
return localStorageData
}
function saveLocalStorageDataTwo ({data}){
localStorageDataTwo = {
data : data,
}
return localStorageDataTwo
}
export { saveLocalStorageData, saveLocalStorageDataTwo };
// Saving to localStorage:
let localData = require("../../support/localStorage");
const data = "Some Data 260-255"
const localStorageData = localData.saveLocalStorageData({ autoId });
window.localStorage.setItem('localStorageData ', JSON.stringify(localStorageData ));

You simply don't use any strict params like {autoId, quoteId, taskId} just pass any arbitrary data.
Don't call something saveLocalStorageData if that's actually not what that function does.
Instead:
const LS = {
set(key, data) { localStorage[key] = JSON.stringify(data); },
get(key) { return JSON.parse(localStorage[key]); },
};
// export { LS };
// import { LS } from "./localstorage.js"
// Example saving multiple data in different LS keys
LS.set("one", {autoId: 1, quoteId: 2, taskId: 3});
LS.set("two", {autoId: 7});
// Example saving Object, and later update one property value
const data = {a: 1, b: 2, c: 3};
LS.set("single", data); // Save
LS.set("single", {...LS.get("single"), c: 99999}); // Update
console.log(LS.get("single")); // {a:1, b:2, c:99999}
// Example saving multiple data into a single Array:
LS.set("arr", []); // Save wrapper Array
LS.set("arr", LS.get("arr").concat({a: 1, b: 2}));
LS.set("arr", LS.get("arr").concat({e: 7, f: 9}));
console.log(LS.get("arr")); // [{"a":1,"b":2}, {"e":7,"f":9}]
jsFiddle playground
or in the last example instead of an Array you could have used an Object. It all depends on the needs.

Related

Printing deepcopy of an object using custom function

I need to print elements of an object which is a deepcopy of another object using custom function. I am able to create deep copy using JSON parse/stringify trick but unable to implement print function.
var obj = {a:1,
b:{
a:2,
c:[1,2,3],
d:{
a:3
}
}
};
const mySnapShot = new Snapshot(object);
mySnapshot.print('a')// 1
.print('b.c') //[1,2,3]
.print('b.a') // 2
Arguments passed in print method are string. Snapshot is a class which contains methods for deep copy and print.
You can use reduce:
var obj = {
a: 1,
b: {
a: 2,
c: [1, 2, 3],
d: {
a: 3
}
}
};
function print(path) {
const pathTokens = path.split('.');
const pathValue = pathTokens.reduce((subObj, pathToken) => {
return subObj && subObj[pathToken] || null
}, obj);
return pathValue;
}
console.log(print('a')) // 1
console.log(print('b.c')) //[1,2,3]
console.log(print('b.a')) // 2

How to remove empty query params using URLSearchParams?

I was working with query params, and got introduced to URLSearchParams. I am using it to form this kind of object to query,
const x = {
a: 'hello World'
b: 23
c: ''
}
let params = new URLSearchParams(x);
console.log(params.toString()) // a=hello+World&b=23&c=
Here, I dont want to have that c=, as it's ugly, and my API doesn't need that.
So, I want this result a=hello+World&b=23 (without empty query string)
But, I couldn't find anything on the MDN Web Docs.
How am I supposed to do that?
Doing the following doesn't work, as it seems to directly mutate the params which affects the forEach:
const x = {
a: 'hello World',
b: '',
c: ''
};
let params = new URLSearchParams(x);
params.forEach((value, key) => { // never reaches `c`
console.log(key, ' => ', value)
if (value == '')
params.delete(key);
});
console.log(params.toString());
You can iterate over the key-value pair and delete the keys with null values:
const x = {
a: 'hello World',
b: '',
c: ''
};
let params = new URLSearchParams(x);
let keysForDel = [];
params.forEach((value, key) => {
if (value == '') {
keysForDel.push(key);
}
});
keysForDel.forEach(key => {
params.delete(key);
});
console.log(params.toString());
A clean way I do it myself is as follows (using lodash):
import omitBy from 'lodash/omitBy';
import isEmpty from 'lodash/isEmpty';
const x = {
a: 'hello World'
b: 23
c: ''
}
const params = new URLSearchParams(omitBy(x, isEmpty));
// mixing other sets
const params = new URLSearchParams({
otherParam: 'foo',
...omitBy(x, isEmpty)
});
Simple way to delete useless params from query in JavaScript ES5+:
for (let param in query) { /* You can get copy by spreading {...query} */
if (query[param] === undefined /* In case of undefined assignment */
|| query[param] === null
|| query[param] === ""
) {
delete query[param];
}
}
return new URLSearchParams(query).toString();
In case your are working with the query as a string you can also filter it with a regex :
const query = "a=hello+World&b=23&c=&d=12&e="
query.replace(/\w+=&/g, '').replace(/&\w+=$/, '')
// "a=hello+World&b=23&d=12"

How to go about mapping an array object within an object using prototype?

I have some issues with getting prototype map function to work with my array object inside my object. I get the error "x() is not a function". I know that you cant use prototype on objects but arrays within objects should be reachable using obj.arr.map().
Here is my code:
let data = [
{'a':1, 'b':2, 'c':3},
{'a':4, 'b':5, 'c':6},
{'a':7, 'b':8, 'c':9}
]
let mapped = data.map(function(data){
let newMap = {}
newMap['a']=data['a']
newMap['b']=data['b']
return newMap;
});
Mapping.prototype.protoMap = function(){
//I have tried writing it as map.data
let protoMap = map.map(function(map){
let protoMap1 = {}
protoMap1['a'] = map.mappedData['a']
return protoMap1;
});
}
function Mapping(data = []){
this.mappedData = data
};
let map = new Mapping(mapped);
Try to stay away from using global variables:
Mapping.prototype.protoMap = function() {
// DON'T do this
// plus, this really doesn't make sense
// because `map` refers to the new `Mapping` object that
// you created; what you probably want to do is use the `mappedData`
// property on your newly created `Mapping` object
// const protoMap = map.mappedData.map(function(map) {...})
// instead, use `this` to access the `mappedData` property
// that you passed to the constructor
const protoMap = this.mappedData.map(function(item) {...})
}
const map = new Mapping(mapped)
See comments in the code snippet to figure out how to fix your example.
function Mapping(data = []) {
this.mappedData = data
}
Mapping.prototype.protoMap = function() {
// access the `mappedData` that was passed
// to the `Mapping` constructor using `this`
const protoMap = this.mappedData.map(
function(item) {
// use `item` argument for the `mappedData.map`
// callback to access each item inside `mappedData`
// individually
const temp = {}
temp["a"] = item["a"]
return temp
}
)
return protoMap
}
const data = [
{'a': 1, 'b': 2, 'c': 3},
{'a': 4, 'b': 5, 'c': 6},
{'a': 7, 'b': 8, 'c': 9}
]
const mapped = data.map(
function(data) {
const newMap = {}
newMap['a']=data['a']
newMap['b']=data['b']
return newMap;
}
)
const mapping = new Mapping(mapped)
const result = mapping.protoMap()
console.log('result', result)
You can use something similar to convert array to map. I have created this util long back ago.
More uses:
https://gist.github.com/deepakshrma/4b6a0a31b4582d6418ec4f76b7439781
class Mapper {
constructor(array, key) {
this.map = array.reduce((map, item) => {
const val = item[key];
if (!map[val]) {
map[val] = [];
}
map[val].push(item);
return map;
}, {});
}
find(key) {
return this.map[key] && this.map[key][Mapper.FIRST_INDEX]; //return blank array
}
findAll(key, returnUndefined) {
//return blank array
return this.map[key] ? this.map[key] : returnUndefined ? undefined : [];
}
}
Mapper.FIRST_INDEX = 0;
let data = [
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
{ a: 7, b: 8, c: 9 },
];
var dataMap = new Mapper(data, "a");
console.log(dataMap.map);
if(typeof window !== 'undefined') window.Mapper = Mapper;

ReactJS: How to access and update nested state object with dynamic key?

Suppose I have a component with state defined as follows:
this.state = {
apple:{
a:1,
b:2,
},
mango:{
banana : {
a:1,
b:2,
}
}
}
If I wanted to update the value of a nested object in my state, I could do so with hard coded keys as shown below:
cost temp = { ...this.state['mango'] }
temp['banana']['a'] = 2;
this.setState({mango:temp});
How would I update a nested value in my state object dynamically key? For example, if I had a JSON path in either dot or array notation, how could I update my component state?
One way to achieve this would be to acquire the nested object that is the parent of the field that your path is targeting via Array#reduce:
const nestedObject = path
.slice(0, -1)
.reduce((object, part) => (object === undefined ? undefined : object[part]), { ...state })
And then update the last key/value of nestedObject by via the last key of your path:
/* Get last part of path, and update nestedObject's value for this key, to 2 */
const [pathTail] = path.slice(-1);
nestedObject[pathTail] = 2;
The following snippet shows these two ideas together:
/* Path of nested field to update, in array notation */
const path = ['mango', 'banana', 'a'];
/* Components state */
const state = {
apple: {
a: 1,
b: 2,
},
mango: {
banana: {
a: 1,
b: 2,
}
}
};
const stateClone = { ...state };
/* Aquire the parent object (ie banana) of the target field (ie a) */
const nestedObject = path
.slice(0, -1)
.reduce((object, part) => (object === undefined ? undefined : object[part]), stateClone)
if (nestedObject !== undefined) {
/* Obtain last key in path */
const [pathTail] = path.slice(-1);
/* Update value of last key on target object to new value */
nestedObject[pathTail] = 2;
}
/* Display updated state */
console.log('Updated state:', stateClone)
/* Call this.setState: */
// this.setState(stateClone);
Update
Here is some extra detail outlining how the reduce() part of the answer works:
path
/* slice obtains ['mango', 'banana'], seeing -1 clips last item */
.slice(0, -1)
/* reduce iterates through each part of array ['mango', 'banana']
where at each iteration we fetch the corresponding nested object
of the { ...state } object that's passed in */
.reduce((object, part) => {
/* At iteration 1:
object has two keys, 'apple' and 'mango'
part is 'mango'
object is defined, so return object['mango'] for first iteration
At iteration 2:
object passed from last iteration has one key, 'banana'
part is 'banana'
object is defined, so return object['banana'] for second iteration
Reduce complete:
we return object['banana'], which is the same as state['mango']['banana']
*/
if(object === undefined) { return undefined; }
return object[part]
}, stateClone)
Having:
const [formState, setFormState] = useState(
{
id:1,
name:'Name',
innerObjectName: {
propA: 'Something',
propB: 'Another thing',
}
});
Maybe you're looking for something like this:
const handleComplexInputChange = (evt, object) => {
setFormState({
...formState,
[object] : {
...formState[object],
[evt.target.name]: evt.target.value,
}
})
}
And from your component you should call it like this:
onChange={(e) => {
handleComplexInputChange(e, "innerObjectName");
}}

Convert JSON object to object which not in JSON format

I have a JSON object with the structure as below
const inputObj = {
"prop1": "val1",
"prop2": {
"prop2_1": "val2_1",
"prop2_2": "val2_2"
}
"prop3": "val3"
}
My objective: I would like to take the property, including the nested property, and store the result in a txt file, but not in JSON format. To make it clear, here is my expected output in the txt file:
{
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
}
prop3: {
id: 'prop3'
}
}
So far, I could write the non nested property, but still not in the structure which I expected. Here is the result so far:
{
"prop1": "prop1",
"prop3": "prop3"
}
Its still in JSON format, not in the structure that I expected, and the nested property still not caught (I still thinking how to get it)
here is the code so far to make my current result:
const fs = require('fs')
const fileName = "./results.txt"
function getAllKeys(obj, path = [], result = []) {
Object.entries(obj).forEach(([k, v]) => {
if (typeof v === 'object') getAllKeys(v, path.concat(k), result)
else result.push(path.concat(k).join("."))
})
return result
}
const inputToFile = getAllKeys(inputObj)
// console.log(inputToFile)
// result of the console.log
// prop1
// prop2.prop2_1
// prop2.prop2_2
// prop3
const newObj = {}
for (var i = 0; i < inputToFile.length; i++) {
var input = inputToFile[i]
var dotIndex = input.indexOf('.') // to check if its from the nested JSON property of the inputObj
if (dotIndex === -1) {
// no dot or nested property in the JSON
newObj[input] = input.toString()
} else {
// if the input contain dot, which is a nested JSON
}
}
fs.writeFileSync(fileName, JSON.stringfy(newObj))
// if I use above line, the result in the file is as I had mention above. But, if the code is like below:
const finals = JSON.stringfy(newObj)
fs.writeFileSync(fileName, JSON.parse(finals))
// the output in the file is only "[Object object]" without double quote
Update
The reason why I need the result to be formatted like that, is because I want to use react-intl. I already have the locale file (the translation), which looks like the inputObj (the structure). Then, I need to make a file, which like this (below), so the lib could translate it:
import { defineMessages } from 'react-intl';
const MessagesId = defineMessages({
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
},
prop3: {
id: 'prop3'
}
})
export default MessagesId;
Thats why, I need it to be not like JSON. Because I already have thousand codes for the translation, but need to define it in the MessagesId. It would be so much takes time rite if I do it manually .__.
Ps: the react-intl is works, the problem is only the converting as my initial questions
This script can handle multiple levels of nestied object.
const outputObj = {};
const convertNestedObj = (obj, parentKey = []) => {
for (key in obj) {
newParentKey = [...parentKey, key];
if (typeof obj[key] === 'object') {
convertNestedObj(obj[key], newParentKey);
} else {
outputObj[newParentKey.join('_')] = { id: newParentKey.join('_') };
}
}
};
convertNestedObj(inputObj);

Categories

Resources