Reduce with obj key, value not working as expected - javascript

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.

Related

Trying to swap key value pairs of an object. Any advices?

Trying to swap key-value pairs of an object!
// an object through we have to iterate and swap the key value pairs
const product = {
id: "FRT34495",
price: 34.56,
nr: 34456,
};
// A function that actually swap them, but don't delete old properties
const change = () => {
for (let key in product) {
const x = key;
key = product[key];
product[key] = x;
}
return product;
};
console.log(change());
//
{
'34456': 'nr',
id: 'FRT34495',
price: 34.56,
nr: 34456,
FRT34495: 'id',
'34.56': 'price'
}
the problem is that I need the object with key-value pairs swapped, but in the same amount, not twice more as we can see above, I need to delete the old ones.
Any advice guys?
Most logically straight-forwarding solution:
Turn object into entries array ([[key1, val1], [key2, val2], ...]) using Object.entries
Swap each of the entries ([[val1, key1], [val2, key2], ...]) using map
Turn back into object using Object.fromEntries
function swapKV (obj) {
const entries = Object.entries(obj)
const swappedEntries = entries.map([k, v] => [v, k])
const swappedObj = Object.fromEntries(swappedEntries)
return swappedObj
}
...or more concise:
const swapKV = obj => Object.fromEntries(Object.entries(obj).map([k, v] => [v, k]))
(Of course, another solution would be to just add if (String(x) !== String(key)) delete product[x] to your code. The condition is there to avoid deleting the entry altogether in case key and value are equal when converted to a string.)
This may help!
const swapKV = (obj) => {
const newObj = {};
for (let val in obj) {
newObj[obj[val]] = val;
}
return newObj;
}
You can also try this:
const swap = (obj) => {
return Object.fromEntries(
Object.entries(obj)
.map((key) => key.reverse())`.concat(Object.entries(obj))`
);
};
If the original object is to be also added to the output, concat can be used. The user asks for a function which also keeps the old properties. Otherwise no need.

Return all existing values as JSON

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)

Destruct a string from split to named object

I am investigating how create a named object in one go, using destruct
A search for javascript destruct* split object did not reveal how to directly destruct a string into an object using split.
I do not mean reduce or map but pure destructing
const list = { ..... } = `......`.split(..)
or at least
const rowItem = { ..... } = `......`.split(..)
My initial version works, but there should be a neater way with less steps
The initial split on lines is fine. It is the filling of the list using destruct I am curious about
const rows = `100|Description 1|a|
101|Description 2|a|
102|Description 3|b|`.split("\n")
let list = {}
rows.forEach(row => {
const [ProductId, Description, subCat, ] = row.split("|")
list[ProductId] = {"Description": Description, "subCat": subCat }
})
console.log(list)
You can do something like this with IIFE
const rows = `100|Description 1|a|
101|Description 2|a|
102|Description 3|b|`.split("\n")
let list = {}
rows.forEach(row => {
(([ProductId, Description, subCat]) => (list[ProductId] = {
Description,
subCat
}))(row.split("|"))
})
console.log(list)
You can consider using Object.fromEntries if it's available in your environment
const rows = Object.fromEntries(`100|Description 1|a|
101|Description 2|a|
102|Description 3|b|`
.split("\n")
.map(row => row.split('|'))
.map(([key, Description, subCat]) => [key, { Description, subCat }])
);
console.log(rows);
It is possible to set the property to an existing object while destructuring.
const row = { Description: 'Description 1', subCat: 'a' },
o = { }; // existing object to be updated;
({ Description: o.Description, subCat: o.subCat } = row);
console.log(o)
In your case, you have dynamic id keys, nested property updates and you are destructuring an array instead of an object. It becomes complex, but it is possible to apply the above logic to your code (This is extremely hackish and purely academic. This should never ever be used in an actual code base)
const str = "100|Description 1|a|",
splits = str.split('|'),
list = {};// existing object
({ 0: id, [-1]: list[id]= {}, 1: list[id].Description, 2: list[id].subCat } = splits)
console.log(list)
This destructures the array like an object. It gets the property with key 0 and sets it to the id variable. Then destructure the -1 property. This is non-existent, so the default value is used and list[id] = {} is set.
This is how the transpiled code looks like:
var _str$split = str.split('|');
id = _str$split[0];
list[id] = _str$split[-1] === undefined ? {} : _str$split[-1];
list[id].Description = _str$split[1];
list[id].subCat = _str$split[2];
console.log(list);
Applying this to your original snippet:
const rows = `100|Description 1|a|
101|Description 2|a|
102|Description 3|b|`
let list = {},
id;
rows.split("\n")
.forEach(str => ({
0: id,
[-1]: list[id] = {},
1: list[id].Description,
2: list[id].subCat
} = str.split('|'))
)
console.log(list)
Another option is to use matchAll and Object.fromEntries()
const str = `100|Description 1|a|
101|Description 2|a|
102|Description 3|b|`;
const regex = /(\d+)\|([^|]+)\|(\w+)/g;
const output = Object.fromEntries(
Array.from(
str.matchAll(regex), ([, id, Description, subCat]) => [id, { Description, subCat }]
)
)
console.log(output)
matchAll is still in Draft stage but it is implemented except by IE and Safari. You can easily polyfill it. It is basically looping through exec results

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, {} )

Get objects in array with duplicated values

I need to get elements from an array of objects where one of that object's properties (name in this case) is duplicated--in other words, appears in some other object in the array.
data
var data = [
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"ram", userid:"SDC"},
{id:1, name:"sam", userid:"CSTR"}
];
i need to check all row and get all the array value where name property is duplicating.
the expected output:
[
{id:1, name:"sam", userid:"ACD"},
{id:1, name:"sam", userid:"CSTR"}
]
my code
Array.from(data).map(x => x.name)
but it is returning all the values.
The code should not create any performance issue because array will contain more than 500 rows.
Angular is a framework, not a language. There is no Angular in your problem.
Let me understand if I understood well. You have an array of objects and you want to keep all the elements that are duplicate and get rid of others, all right? You can try:
data.reduce((acc, value, i, arr) => {
if(acc.some(v => v.name === value.name)) return acc;
let filtered = arr.filter(v => v.name === value.name);
return filtered.length > 1 ? acc.concat(filtered) : acc;
}, []);
Or you can sort your array in first instance, in order to improve performance:
const sort = (a, b) => a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1;
let duplicates = [];
let sortedArray = data.sort(sort);
for(let i=0; i<sortedArray.length - 1; i++) {
if(sortedArray[i].name === sortedArray[i+1].name) {
duplicates.push(sortedArray[i], sortedArray[i+1]);
i++;
}
}
The brute force approach would be to filter the array to keep only those elements with duplicated names, as expressed by the filter function duplicateName.
// Is there more than one element in an array satisfying some predicate?
const hasMultiple = (arr, pred) => arr.filter(pred).length > 1;
// Is this element a duplicate in the context of the array?
const duplicateName = (elt, idx, arr) => hasMultiple(arr, e => e.name === elt.name);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(data.filter(duplicateName));
However, this is going to have poor performance (O(n^2)) in the case of many elements. To solve that problem, you're going to need to preprocess the array. We'll create an object with a property for each name, whose value is an array of all the elements in which that name occurs. This operation is usually called groupBy. Several popular libraries such as underscore will provide this for you. We'll write our own. After grouping, we will filter the object of groups to remove those with only one member.
// Group an array by some predicate.
const groupBy = (arr, pred) => arr.reduce((ret, elt) => {
const val = pred(elt);
(ret[val] = ret[val] || []).push(elt);
return ret;
}, {});
// Filter an object, based on a boolean callback.
const filter = (obj, callback) => Object.keys(obj).reduce((res, key) => {
if (callback(obj[key], key, obj)) res[key] = obj[key];
return res;
}, {});
// Remove groups with only one element.
const removeNonDups = groups => filter(groups, group => group.length > 1);
// Test data.
var data = [
{id:1,name:"sam", userid:"ACD"},
{id:1,name:"ram", userid:"SDC"},
{id:1,name:"sam", userid:"CSTR"}
];
console.log(removeNonDups(groupBy(data, elt => elt.name)));

Categories

Resources