Return all existing values as JSON - javascript

The following code will output all the keys and values form my form:
this.primaryFormGroup.valueChanges.subscribe(inputFields => {
console.log(inputFields);
}
It will output all the values, even if they are empty but that is not what I want.
Object.entries(inputFields).filter(([key, value]) => value)
This will output only the values, which are not empty. So far, everything is good.
My problem
The output is always an array but what I want is JSON.
I have tried it with JSON.stringify(Object.entries(inputFields).filter(([key, value]) => value)); and this is how the output will be:
[["title","Hello"],["description","I am a
text"],["tech",["javaScript","CSS"]]]
In my case the value of the key tech should be an array, since it is already defined as array but everything else should be JSON.
How can I do this?

To filter out all null or empty values you could try the following:
this.primaryFormGroup.valueChanges.subscribe(inputFields => {
const clean = Object.keys(inputFields).filter(k => !!inputFields [k]).reduce((acc, curr) => {
acc[curr] = inputFields[curr];
return acc;
}, {});
}
Note:
This solution will not work with deep forms.

// assuming initial values of all form controls is undefined.
const formValues = this.primaryFormGroup.value;
const formValuesWithNoundefinedKeys = JSON.parse(JSON.stringify(formValues));
// formValuesWithNoUndefinedKeys won't contain any keys with undefined values
If you need to remove null values as well, you can provide a REPLACER function to JSON.stringify function.
Example snippet below:
const obj = {a: undefined, b:1}
const objWithNoUndefinedValues = JSON.parse(JSON.stringify(obj))
// objWithNoUndefinedValues => {b: 1}
console.log(objWithNoUndefinedValues)

Related

Error with splice method: can't define array index property past the end of an array with non-writable length

I want to insert a string value inside an array with 3 string values randomly. But it gives this error in console:
*Uncaught TypeError: can't define array index property past the end of an array with non-writable length*
After assigning obj.incorrect_answers to answers, the length of the array will be 3, and I try to insert correct_answer(that is a string) in an index between 0 to 3.
It's desired part of my code:
export const Home = () => {
const [id, setId] = useState(0);
const dispatch = useDispatch();
const obj = useSelector((state) => state.questions.data[id]);
const fetchStatus = useSelector((state) => state.questions.status);
if(obj){
let answers = obj.incorrect_answers;
answers.splice(Math.floor(Math.random()*4), 0, obj.correct_answer);
console.log(answers);
}
useEffect(() => {
if(fetchStatus === "idle"){
dispatch(fetchQuestions());
}
}, [fetchStatus])
obj is received from the server, but I know it's received correctly because when I console.log obj.incorrect_answers it gives me an array from 3 strings and when I console.log obj.correct_answer it gives me a string, correctly. So I think I just have a problem with splice method.
Maybe, it is relevant to receive data from the server, but I can see received data in the console...
Judging by the error message, it seems you have a read-only length prop on your array! try to make it writable: Object.defineProperty(answers, 'length', { writable: true });
An alternative way of getting the same result is to insert to the end of the array, then simply shuffle the array.
if(obj){
let answers = obj.incorrect_answers;
answers.push(obj.correct_answer)
answers.sort(() => Math.random() - 0.5)
console.log(answers);
}
It fixed by slice() Only by changing:
let answers = obj.incorrect_answers;
to:
let answers = obj.incorrect_answers.slice();

Reduce with obj key, value not working as expected

I'm trying to dynamically create a sqlite insert string with node. I have:
function update_json_obj_into_table(tablename, obj, search_condition) {
const insertValues = Object.entries(obj).reduce((acc, [k,v]) => acc.push(`${k} = ${v}`), []);
const insertValuesString = Array.prototype.join.call(insertValues, '/');
console.log(insertValuesString);
console.log(`UPDATE ${tablename} SET ${insertValues} WHERE ${search_condition}`);
// db.run(`UPDATE ${tablename} SET ${insertValuesString} WHERE search_condition ;`);
};
When I run it :
const obj = {Code: 'A1'};
update_json_obj_into_table('mytable', obj, 'search_condition')
I get:
UPDATE mytable SET 1 WHERE search_condition.
Obviously the insertValues is not working correctly. I was expecting:
Code = 1
but its coming out as '1' . What am I doing wrong?
.push returns the new length of the array, not the mutated array. While you could put the .push on its own line and return the array on the next line:
function update_json_obj_into_table(tablename, obj, search_condition) {
const insertValues = Object.entries(obj).reduce((acc, [k, v]) => {
acc.push(`${k} = ${v}`);
return acc;
}, []);
const insertValuesString = Array.prototype.join.call(insertValues, '/');
console.log(insertValuesString);
console.log(`UPDATE ${tablename} SET ${insertValues} WHERE ${search_condition}`);
// db.run(`UPDATE ${tablename} SET ${insertValuesString} WHERE search_condition ;`);
};
const obj = {
Code: 'A1'
};
update_json_obj_into_table('mytable', obj, 'search_condition')
It would make a lot more sense to use .map, since the input array and ouput insertValues array are one-to-one:
function update_json_obj_into_table(tablename, obj, search_condition) {
const insertValues = Object.entries(obj)
.map(([k, v]) => `${k} = ${v}`);
const insertValuesString = Array.prototype.join.call(insertValues, '/');
console.log(insertValuesString);
console.log(`UPDATE ${tablename} SET ${insertValues} WHERE ${search_condition}`);
// db.run(`UPDATE ${tablename} SET ${insertValuesString} WHERE search_condition ;`);
};
const obj = {
Code: 'A1'
};
update_json_obj_into_table('mytable', obj, 'search_condition')
That said, dynamically constructed queries like these are rarely a good idea - unless the input is trustworthy, it can compromise your database. Even if the input is trustworthy, it can be inelegant due to delimiter escaping issues, reserved works, and so on. Better to look up how to use prepared statements instead, so that the database driver handles the interpolation of external values.

How to create javascript object from concatenated key value pairs

I need to create an object from a variable created with concateneated key value pairs.
HardCoded (it is an array as dataSource for a jquery datatable), it looks like this:
obj = {
totalValue: valVar,
totalReceiptValue: receiptVar
}
dataSourceArray.push(obj);
My variable is:
cumulator ="totalValue:8573.0000,totalReceiptValue:3573.00,"
I've tried this,
var obj = {};
const name = "";
const value = cumulator;
obj[name] = value;
dataSourceArray.push(obj);
but then I have an extra key i.e. "": in the object which of course fails for my requirements.
How do I do this?
Thanks
Assuming that there are no extra : or , signs that could mess with the logic, it can be achieved simply by spliting the strings and using the built in method Object.prototype.fromEntries
const input = "totalValue:8573.0000,totalReceiptValue:3573.00,";
const output = Object.fromEntries( // combine entries into object
input.split(",") // divide into pairs
.filter(Boolean) // remove empty elements (comma at the end)
.map((el) => el.split(":")) // divide the pair into key and value
)
console.log(output)
There apparently being some problems with IE and Edge, the code above can be fixed by writing own implementation of from Entries and opting to not use arrow functions.
const input = "totalValue:8573.0000,totalReceiptValue:3573.00,";
const entries = input.split(",") // divide into pairs
const output = {};
for (let i=0; i<entries.length; i++) {
if (!entries[i]) continue; // remove empty elements (comma at the end)
const [key, val] = entries[i].split(":"); // divide the pair into key and value
output[key]=val; // combine entries into object
}
console.log(output)
You can simply use split, map reduce functions.
Try this.
const input = "totalValue:8573.0000,totalReceiptValue:3573.00,";
const result = input.split(',').filter(Boolean).map(function(text) {
return text.split(':')
}).reduce(function(acc, curr) {
acc[curr[0]] = curr[1]
return acc
}, {})
console.log(result)

Split an array into small arrays based on text in values

I have big array, which looks like this example:
let array = ['aa-we', 'aa-we__qq', 'aa-we__qw', 'gsPlsOdd', 'bc-po-lp', 'bc-po-lp--ps', 'de', 'de__io', 'de__sl', 'de--xz', 'ccsDdd'];
i want split this array into small arrays by values:
let array = [
['aa-we', 'aa-we__qq', 'aa-we__qw'],
['bc-po-lp', 'bc-po-lp--ps'],
['de', 'de__io', 'de__sl', 'de--xz']
]
// and camelcase strings should be removed
Values in array have syntax like BEM selectors, so if the prefix of different strings is the same, they should be wrapped in a single array.
How can i do this, if possible, without additional libraries?
Thanks for the help or tips!
console.clear()
let data = [
"aa-we",
"aa-we__qq",
"aa-we__qw",
"gsPlsOdd",
"bc-po-lp",
"bc-po-lp--ps",
"de",
"de__io",
"de__sl",
"de--xz",
"ccsDdd",
];
resultO = data.reduce((acc, val, idx) => {
if (val.match(/[A-Z]/)) {return acc;}
const sel = val.replace(/^(.*)(__|--).*$/g, "$1");
acc[sel] = acc[sel] || [];
acc[sel].push(val)
return acc;
}, {})
resultA = Object.values(resultO)
console.log(resultA)
I'd do something like this and then filter out what you don't want.
let array = ['aa-we', 'aa-we__qq', 'aa-we__qw', 'gsPlsOdd', 'bc-po-lp', 'bc-po-lp--ps', 'de', 'de__io', 'de__sl', 'de--xz', 'ccsDdd'];
array = array.filter((a) => !a.match(/[A-Z]/))
let result = groupBy(array, (str)=> str.split(/[-_]/)[0])
console.log(Object.values(result))
function groupBy(arr, condition) {
return arr.reduce((result, current) => {
const key = condition(current);
(result[key] || (result[key] = [])).push(current)
return result
}, {})
}
The algorithm can be as follows:
Create Map<Prefix,ValuesArray>
For each element in array:
Get it's prefix, e.g. "ab", skip element if invalid (e.g. no prefix exist or camel case)
Add to corresponding hashed bucket
Join values from Map into one array
JS has all the primitives to implement this, just take a look at Map/Object for hashing and Array (map/filter/reduce) for processing.

JSON.stringify(localStorage) - filtering by key

I use a small code snipped to save the localStorage of my application as a string:
var saveStr = JSON.stringify(localStorage);
It works great at a first glance, but it basically dump the entire localStorage object, which I don't want. I'd like to stringify the localStorage, but only the keys that contains a certain string.
For instance:
var saveStr = JSON.stringify(filteredLS("example"));
filteredLS should return the localStorage data, but only the keys that contains the string that was passed as an argument.
Someone knows an easy snipped to achieve this?
Thanks!
Try this
function filteredLS(term) {
var filteredObj = {};
Object.keys(localStorage)
.filter(function (key) {
return key.indexOf(term) >= 0;
})
.map(function (key) {
filteredObj[key] = localStorage.getItem(key);
});
return JSON.stringify(filteredObj);
}
You should use the methods localStorage.getItem and localStorage.setItem. With those, you can write your own get & set functions to easily use JSON objects:
function get(item) {
return JSON.parse(localStorage.getItem(item))
}
function set(item, value) {
return localStorage.setItem(item, JSON.stringify(value))
}
// use like this:
set('foo', { bar: 1 })
var result = get('foo')
// result: { bar: 1 }
Depending on your target browser you may want to transpile this, but for brevity I'm going with a (mostly) es6 style - this should run in modern browsers
Filtering an object by keys:
const filterByKeys = obj => keys => Object.entries(obj)
// keep only the keys we care about
.filter( ([key, val]) => keys.includes(key) )
// make a new object with just the filtered keys
.reduce( (accum, [key, val]) => Object.assign(accum, {[key]:val} ), {} )
Usage:
// create a function for getting select keys
const localStore = filterByKeys(localStorage)
// call that function with a list of keys you want
const myValues = localStore(['foo', 'bar'])
// and JSON for completeness
const localStoreJson = keys => JSON.stringify(localStore(keys))
Alternate option if you're transpiling or reading this in the future - using spread operator and compacting filter+reduce into one step - for your purposes this is likely unnecessary:
const filterByKeys = obj => keys => Object.entries(obj)
// filter and reduce in one step
.reduce( (accum, [key, val]) => keys.includes(key) ? {...accum, [key]:val } : accum, {} )

Categories

Resources