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
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
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"
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;
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");
}}
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);