Changing key value of object without discarding rest of keys - javascript

I would like to replace spaces with underscores for a specific key in all incoming objects. It works, however the rest of the keys dissappear.
The objects:
{
"id": "235",
"reference": "AA",
"name": "Jake H",
},
{
"id": "668",
"reference": "TC",
"name": "Felix S",
}
Actual outcome:
["Jake_H", "Felix_S"]
Method:
import jsonResults from './results.json'
data() {
return {
results: [],
}
},
mounted() {
const filteredResults = jsonResults
// I have other incoming objects that do not have names.
.filter(result => result.name)
.map(
result => result.name.replace(' ', '_')
)
this.results = filteredResults
}
I expect just the key value to change but what happens is the rest of the object is discarded.
Expect
{
"id": "235",
"reference": "AA",
"name": "Jake_H",
}
Actual
["Jake_H"]

You're returning result.name.replace(...) due to the implicit return of the ES6 arrow function - return result after modifying it. Destructuring is useful here, as is spreading:
.map(({ name, ...r }) => ({ ...r, name: name.replace(/ /g, "_")));
Alternate way:
.map(result => {
result.name = result.name.replace(/ /g, "_");
return result;
});

You need to return other properties as well in the map method, together with the modified name property.
const filteredResults = jsonResults
.filter(result => result.name)
.map(
result => {
return {
...result,
name: result.name.replace(' ', '_')
}
}
)

You are returning the just the name in the map funciton:
result => result.name.replace(' ', '_')
So rather do:
result => { result.name = result.name.replace(' ', '_'); return result; }

Related

How to filter json data with multiple includes values

Includes method working for only one Value but i want to filter multiple values with comma like this "mahmoud, faizan" i'll get these value from user input
json Data
[{
"text": "adnan hassan"
}, {
"text": "adnan hassan mahmoud",
}, {
"text": "adnan hassan faizan",
}]
Filter Function
const filterFunction = (a) => {
if(includeKeyword)
return a.text.includes("mahmoud")
}
Filter Function with map render
postRes.filter(filterFunction).map((data, index) => (
I gave you two return statements to choose from, depending on if you want to match all parts or at least one part out of what was typed in:
const filterFunction = (a) => {
if(includeKeyword)
const parts = userInput.split(",");
// if you need the returned element to match *all* parts (AND)
return parts.every(p => a.text.includes(p.trim());
// if you need it to match *at least one* part (OR)
return parts.some(p => a.text.includes(p.trim());
}
};
for your senario you can customize the filter function to get a second parameter and use array.some:
const filterFunction = (a, searchArray) => {
if (searchArray.some((v) => a.text.includes(v))) {
return a;
}
};
const result = postRes.filter((item) => filterFunction(item, ["mahmoud", "faizan"]));
console.log("result ", result);
Filter function returns a boolean. You can set this boolean with a loop :
Example :
const base = [{
"text": "adnan hassan"
}, {
"text": "adnan hassan mahmoud",
}, {
"text": "adnan hassan faizan",
}]
function myFilter(base,query){
const queries = query.split(",")
return base.filter((a)=>{
let doReturn = false;
queries.forEach((q)=>{
if(a.text.includes(q)){
doReturn = true;
}
})
return doReturn;
});
}
console.log(myFilter(base,"faizan,hector,mickey,mahmoud"))
create a array from your search text with split(','):
const searchParams = searchText.split(',');
then filter your data array like this:
test is your data array searchParams is the list of search parameters:
test.filter(t => searchParams.some(v => t.text.includes(v)))
here is a little example
if you like to solve by javascript methods, like many people pointed out
if (["mahmoud", "faizan"].some((v) => a.text.includes(v))) {
return a;
}
if you like regular expression, do this way
if(a.text.match(/(mahmoud|faizan)/)){
return a
}

Converting object values and properties into an array

How to convert an object with names and values into an array of object just like the below format.
'result' : { "name1" : "Angle", "name2" : "Demon", "name3" : "Hunter"}
Desired output :
"result" : [
{'name1' : 'Angle'},
{'name2' : 'Demon'},
{'name3' : 'Hunter'}
]
You can use Object.entries and Array#map methods as follows:
const input = {'result' : { "name1" : "Angle", "name2" : "Demon", "name3" : "Hunter"}}
const output = [input].map(
({result}) =>
({result: Object.entries(result).map(([k,v]) => ({[k]:v}))})
)[0];
console.log( output );
const result = { "name1" : "Angle", "name2" : "Demon", "name3" : "Hunter"};
const res = Object.keys(result).map(item => {
const obj = {};
obj[item] = result[item]
return obj;
});
console.log(res);
Using Object.entries:
const input = { "name1": "Angle", "name2": "Demon", "name3": "Hunter" };
const result = Object.entries(input).map(([k, v]) => ({ [k]: v }));
console.log(result);
Using Object.keys:
const input = { "name1": "Angle", "name2": "Demon", "name3": "Hunter" };
const result = Object.keys(input).map(k => ({ [k]: input[k] }));
console.log(result);
A breakdown of the syntactic gymnastics of the arrow function given to map.
Notice where we have:
.map(([k, v]) => ({ [k]: v })
Basically, each element of the array returned by Object.entries is itself an array with two values, the first is the property name (key) and the second is the property value. For this example, Object.entries(input) returns:
[
[ "name1", "Angle" ],
[ "name2", "Demon" ],
[ "name3", "Hunter" ]
]
But we want to turn ["name1", "Angle"] into an object like { name1: "Angle" }.
The most straightforward way of expressing this would be:
Object.entries(input).map(entry => {
return { [entry[0]]: entry[1] };
}
The only tricky part in the syntax above is creating a dynamic property name based on a variable with the syntax { [key]: value }. We want a property named entry[0] with the value in entry[1] and { [entry[0]]: entry[1] } will do that.
But we can make use of some destructuring and return the object from the arrow function directly.
destructuring. Rather than using entry as the parameter, we can destructure this short 2-element array into the key and value immediately. Although it's tempting to write [k, v] => you must enclose it in parentheses like ([k, v]) => .
returning an object from an arrow function. It's also tempting to return an object literal like => { name1: "Angle" } but again, that's ambiguous (looks like a code block) so we have to enclose the object literal with parentheses: => ({ name1: "Angle" })
All those extra parentheses are unfortunately necessary. The whole thing looks like:
Object.Entries(input).map(([k, v]) => ({ [k]: v }));
So perhaps you may find the destructuring syntax is clunky because of the parentheses. Instead you can use Object.keys. Object.keys(input) returns:
[
"name1",
"name2",
"name3"
]
We can map each property name to the desired object like this .map(k => ({ [k]: input[k] })) Which saves us a little bit of destructuring syntax awkwardness at the cost of having to specify the array by its name again.
This is likely the fastest way, if the number of properties is large, because it should use fewer allocations and intermediate objects.
Alternate approach with a loop
There is another way, using a loop, and it's faster than both of the above if the number of properties is very small.
const input = { "name1": "Angle", "name2": "Demon", "name3": "Hunter" };
const result = [];
for (let key in input) result.push({ [key]: input[key] });
console.log(result);
(This actually performs best on your tiny test data, I found.)
But I personally prefer functional constructs over imperative constructs because it gives the compiler the opportunity to do something more efficient than a loop. I believe we should teach the next generation of programmers to embrace modern ways of describing programs, and loops are passé in that regard.
Using Object.fromEntries and Object.entries
const input = { "name1": "Angle", "name2": "Demon", "name3": "Hunter" };
const result = Object.entries(input).map((ent) => Object.fromEntries([ent]));
console.log(result);

How to add dynamic keys with nested level from the array in javascript

Hi I want to create new object on the basis of path array. It will be dynamic. I tried with reduce but it is not giving correct result.
const obj = {
"e": [{
"name": "moviename",
"path": ["a"]
},
{
"name": "hero",
"path": ["a", "b"]
},
{
"name": "desc",
"path": ["c"]
},
{
"name": "udf",
"path": []
}
]
}
// this is what i want this object to be created programatically. after parsing above obj.
const output = {
"a": {
"moviename": "",
"b": {
"hero": ""
}
},
"c" : {
"desc": ""
},
"udf": ""
}
const payload = {};
obj.e.forEach((item) => {
if (item.path.length === 0) {
payload = {
...payload,
[item.name]: ''
};
} else {
item.path.reduce((o, s, index) => {
if ((index + 1) === item.path.length) {
return o[s] = {
[item.name]: ''
};
}
return o[s] = {};
}, payload);
}
});
console.log(payload);
You can use simple for loops -- reduce would also work (see further down), but I think the overhead of a callback is not worth it:
function convert(arr) {
const output = {};
for (const {name, path} of arr) {
let node = output;
for (let prop of path) {
node = (node[prop] ??= {});
}
node[name] = "";
}
return output;
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
With reduce:
Using reduce it would translate to this:
function convert(arr) {
return arr.reduce((output, {name, path}) => {
let node = output;
for (let prop of path) {
node = (node[prop] ??= {});
}
node[name] = "";
return output;
}, {});
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
With double reduce:
If the inner loop is also done through reduce, then:
function convert(arr) {
return arr.reduce((output, {name, path}) => {
path.reduce((node, prop) => node[prop] ??= {}, output)[name] = "";
return output;
}, {});
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
The logical nullish assignment operator
If your environment has no support for ??= then use one of the following alternatives:
node[prop] ||= {}
(node[prop] = node[prop] ?? {})
(node[prop] = node[prop] || {})
Some comments on your code
As this function builds the object from scratch, it is not really necessary to treat intermediate versions of the object as immutable -- as your code attempts to do at least in the case of path.length == 0: just keep extending the object through mutation.
return o[s] = {}; is destructive: if the property was already created from a previously processed path, then this will overwrite whatever was already assigned to o[s].

maintain key format when copying values from one object to another

The following code demonstrates an issue that has surfaced while writing an app using the Vue framework for the front end. The issue is really a JS one though.
Here is the data object needed by the Vue component:
let data = { accountId: '', prospectId: '', address: '', city: '', state: '' }
and here is an object containing a row of data from the database:
const retrieved = {
"ProspectID": "4",
"AccountID": "1003",
"Address": "E2828 Highway 14",
"City": "Madison",
"State": "WI",
"Created": "2021-02-27 11:49:33.523",
"Updated": "2021-02-27 11:49:33.523"
}
It is necessary to copy some of the values from retrieved into data. The current way of doing the copy is the following:
data.accountId = retrieved.AccountID;
data.prospectId = retrieved.ProspectID;
data.address = retrieved.Address;
data.city = retrieved.City;
data.state = retrieved.State;
console.log('data', data);
The result of the above code is the desired outcome and it looks like this:
I'm looking for a more efficient way to do the copying because it's tedious when there are many key/value pairs involved.
I've tried this:
data = { ...data, ...retrieved };
console.log('data', data);
which results in this
which basically unions all the key/value pairs together. Not the desired outcome.
It is critical that the key names in data keep their exact names and no extra key/value pairs get added to data. How can this be achieved?
Since the capitalization is different, spread won't work. You'll have to iterate over an array mapping the properties on the different objects to each other:
const propsToCopy = {
// data // retrieved
accountId: 'AccountID',
prospectId: 'ProspectID',
// ...
};
for (const [dataProp, retrievedProp] of Object.entries(propsToCopy)) {
data[dataProp] = retrieved[retrievedProp];
}
That said, having slightly different property names for the same data like this seems very strange, since it makes the code a lot more convoluted than it needs to be and greatly increases the risk of typo-based problems, when a property is capitalized but doesn't need to be, or vice-versa. Consider if you can use just a single property name format instead, if at all possible; then the propsToCopy could be reduced to an array:
const propsToCopy = ['accountId', 'prospectId', /* ... */ ];
You can use a Proxy in order to intercept all settings of values. This allows only setting known values and ignoring anything else.
To make the setting of the property preserve the case, we can just lookup the original key case-insensitively and use the original one.
Finally, using Object.assign() will call all the setters on the target which means that the proxy can intercept these calls:
const eqCaseInsensitive = a => b =>
a.toLowerCase() === b.toLowerCase();
const handler = {
set(target, prop, value, receiver) {
const key = Object.keys(target)
.find(eqCaseInsensitive(prop));
if (key !== undefined) {
return Reflect.set(target, key, value, receiver);
}
return true;
}
}
let data = { accountId: '', prospectId: '', address: '', city: '', state: '' }
const retrieved = {
"ProspectID": "4",
"AccountID": "1003",
"Address": "E2828 Highway 14",
"City": "Madison",
"State": "WI",
"Created": "2021-02-27 11:49:33.523",
"Updated": "2021-02-27 11:49:33.523"
}
Object.assign(new Proxy(data, handler), retrieved);
console.log(data);
This can further be converted to a helper function that is analogous to Object.assign() and allow for as many sources as you wish. To save some processing time, there is no need to do a full search for each property assignment - a simple lookup map can be precomputed that holds lowercase property names as keys and normal case property names as values:
const assignOnlyKnownProps = (target, ...sources) => {
const known = new Map(
Object.keys(target)
.map(key => [key.toLowerCase(), key])
);
const handler = {
set(target, prop, value, receiver) {
const lookup = prop.toLowerCase();
if (known.has(lookup)) {
Reflect.set(target, known.get(lookup), value, receiver);
}
return true;
}
}
return Object.assign(new Proxy(target, handler), ...sources);
}
let data = { accountId: '', prospectId: '', address: '', city: '', state: '' }
const retrieved = {
"ProspectID": "4",
"AccountID": "1003",
"Address": "E2828 Highway 14",
"City": "Madison",
"State": "WI",
"Created": "2021-02-27 11:49:33.523",
"Updated": "2021-02-27 11:49:33.523"
}
assignOnlyKnownProps(data, retrieved);
console.log(data);
Creating a copy of retrieved and converting all the key names to lowercase, then populating the values for data works -
const r = Object.fromEntries(
Object.entries(retrieved).map(([k, v]) => [k.toLowerCase(), v])
);
// Object.keys(data).forEach(key => {
// data[key] = r[key.toLowerCase()];
// });
Object.keys(data).map(k => data[k] = r[k.toLowerCase()]);
console.log('r', r);
console.log('data', data);

Simple method to modify json keys casing to camelCase from snake_case

I have lots of json parsing and all the json I am receiving is having keys in snake case like user_name. And for parsing I need it in camel case like userName.
The sample json file would look like this:
[{
"user_id" : 1,
"user_name" : "Abcd"
},
{
"org_id" : 11,
"org_name" : "some_name"
}
...
]
Expected output:
[{
"userId" : 1,
"userName" : "Abcd"
},
{
"orgId" : 11,
"orgName" : "some_name"
}
...
]
The json I am receiving is not having any particular fixed notation and it can be anything. But all the keys will be in snake case. And I need to convert it to camelCase.
What I cannot do is, find and replace, because it also replace snake casing strings in values as well.
Is there any easy method, which can do the same?
You can use npm package called: camelcase-keys-deep
https://www.npmjs.com/package/camelcase-keys-deep
You can do the following:
var keys = [];//this will contain the json with desired output
for(var i = 0;i<myObj.length;i++)//myObj is the variable that contains your json
{
Object.keys(myObj[i]).forEach(function(key){
if(keys.indexOf(key) == -1)
{
var newValue = {};
var value = myObj[i][key];
key = key.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });
newValue[key] = value;
keys.push(newValue);
}
});
}
//console.log(keys);
Hope this helps :)
So AKSHAY JAIN's implementation is pretty solid but it will delete the properties that are not in snake case. I fixed the implementation.
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
},
{
"personId": 12,
"personName": "otherName"
}];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
if (newK != k) {
a[newK] = a[k];
delete a[k];
}
});
});
console.log(arr);
If u use lodash I would suggest my solution for this:
snake_case -> camelCase
function camelCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => camelCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.camelCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => camelCaseDeep(value));
return objWithMappedValues;
}
camelCase -> snake_case
function snakeCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => snakeCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.snakeCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => snakeCaseDeep(value));
return objWithMappedValues;
}
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
}
];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
a[newK] = a[k];
delete a[k];
});
});
console.log(arr);

Categories