What is the efficient way to shallow clone an object using JavaScript? - javascript

//function for creating a shallow object and remove the multiple spaces
export const getTrimmedValues = (values) => {
const shallowValues = {
...values,
};
for (const key in shallowValues.primaryInformation) {
const currentValue = shallowValues.primaryInformation[key];
if (typeof currentValue === 'string') {
shallowValues.primaryInformation[key] = currentValue.replace(/\s+/g, ' ').trim();
}
}
return shallowValues;
};
//Original Object
const values = {
otherObject: {}
otherArray: []
primaryInformation: {
email: "test.test#testdata.com"
externalId: "DSB-External test"
firstName: "Dave External test test"
isGood: true
isHeaven: false
lastName: "Bacay External"
userId: 656555
}
}
//calling the function
getTrimmedValues(values)
I want to create a shallow object from the original object and edit the string to remove the multiple spaces by using shallow object and for loop, I think I implemented it in a wrong way.
All advices and comments are appreciated.

Here we can take advantage of JSON.stringify function has a second parameter as a replacer function is internally iterating on every key of object.
Please check the below code.
//Original Object
const values = {
otherObject: {},
otherArray: [],
primaryInformation: {
email: "dave.external#testdata.com",
externalEmployeeId: "DSB-External test ",
firstName: "Dave External test test",
isActive: true,
isExternal: false,
lastName: "Bacay External",
userId: 656555,
}
};
function getTrimmedValues(obj) {
let str = JSON.stringify(obj, (key, value) => {
if(typeof value === 'string') {
return value.replace(/\s+/g, ' ').trim()
}
return value;
});
return JSON.parse(str);
}
console.log(getTrimmedValues(values));

Related

Merging values from an array of strings into a nested object in javascript

I want to merge values from an array into a static nested object. The array containing the values is something like this,
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
and the object in which the values has to be merged is,
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
I want my output object to look like below,
const updated_person = {
details_1: {
name: 'ABC XYZ',
hobbies: [M,N,O,P],
profession: 'S'
},
details_2: {
age: 27
}
};
Thanks a lot for your help!
I made another solution with a different approach.
Here I used an interface weher I described the desired data structure.
In the second part the string array is tranformed into key and value pairs. Thereform are filtered the keys of interface and added into an empty object literal.
const data = ["name=ABC XYZ", "hobbies=[M,N,O,P]", "profession=S", "age=27"];
const dataInterface = {
details_1: { name: null, hobbies: null, profession: null },
details_2: { age: null },
};
function orederData(arr) {
const record = arr.map((item) => {
let [key, value] = item.split("=");
if (value[0] === "[" && value[value.length - 1] === "]") {
value = value.slice(1, value.length - 1).split(",");
}
return { key, value };
});
const dataBlock = {};
Object.keys(dataInterface).map((detail) => {
dataBlock[detail] = {};
Object.keys(dataInterface[detail]).forEach((dataKey) => {
dataBlock[detail][dataKey] = record.filter((record) => {
return record.key === dataKey;
})[0].value;
});
});
return dataBlock;
}
const orderedData = orederData(data);
console.log(orderedData);
You can simply achieve this by iterating the input array.
const arr = ['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27'];
const person = {
details_1: {},
details_2: {}
};
arr.forEach(item => {
(item.split('=')[0] !== 'age') ? person.details_1[item.split('=')[0]] = item.split('=')[1] : person.details_2[item.split('=')[0]] = item.split('=')[1]
});
console.log(person);
There is no way to cleanly merge an unstructured array into a structured object such that the array values end up in the appropriately keyed person properties.
javascript does provide the assign() function that merges objects but for YOUR requirements your source data needs to be an object similarly structured and not an array.
so this:
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
would need to become this:
const source= [{details_1: {"name":"ABC XYZ", "hobbies":"[M,N,O,P]", "profession":"S"}, details_2: {"age":"27"}}]
such that a call to Object.assign():
const new_person = Object.assign(person, source[0]);
fills this
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
properly, though you may need to clone or instantiate and empty person first.
or, if person is an Object you could have a fill() method that knows what to do with the array data.

Is there a way to check for falsy values from an array?

I want to check a bunch of variables for falsy values.
Instead of writing a very long if statement, e.g.:
if (!userData.email || !userData.lastName || ! userData.firstName etc...
I wanted to put these values in an array and simply check the array, e.g.:
var detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
if (detailsConditionsArr.indexOf(false) === 1) {
console.log("Found a falsy");
}
But the above doesn't work (if statement never fires).
The values could be false, null or ''.
Is there a way to check these from the array?
Ideal use case for Array#some
Check for falsy value in the iteration, if the test succeeds, true will be returned.
var A = ["Name", ""];
var B = ["Name", "30"];
var C = ["Name", "30", null];
if (A.some(el => !el)) {
console.log("Found a falsy in A");
}
if (B.some(el => !el)) {
console.log("Found a falsy in B");
}
if (C.some(el => !el)) {
console.log("Found a falsy in C");
}
You can use the key names you want to check as an array, then do the check like this:
const userData = { email: '', lastName: '', firstName: '' };
const hasFalsy = ['email', 'lastName', 'firstName'].some(k => !Boolean(userData[k]));
if (hasFalsy) {
console.log('found a falsy');
}
You can use reduce to return a single value from the array like so:
var detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
if (detailsConditionsArr.reduce((carry, el)=> el ? carry : true, false)) {
console.log("Found a falsy");
}
You can loop the list of properties and then check if any is falsy :
const userData = {
firstName : 'John',
lastName : 'Doe',
email : null,
phone : 42,
address : 'Hello',
mailAddress : 'world'
};
function HasObjectAnyFalsyValues(obj)
{
for (let prop in obj)
{
if (Object.prototype.hasOwnProperty.call(obj, prop)
&& !obj[prop]) { // <-- check for falsy
return true;
}
}
return false;
}
console.log('The object has ' + (!HasObjectAnyFalsyValues(userData) ? 'no ' : '') + 'falsy values');
This will check all properties. If you want to list specific properties instead, use one of the other answers posted
You can use some method.
var detailsConditionsArr = [
"test",
12,
false,
"s",
"sd",
"test",
]
if (detailsConditionsArr.some(x => !x)) {
console.log("Found a falsy");
}
// or
//if (detailsConditionsArr.some(x => !Boolean(x))) {
// console.log("Found a falsy");
//}
Using some is a clean way for doing this job.
The Array.prototype.some() method tests whether at least one element in the array passes the test implemented by the provided function (in your case, falsy value).
const userData = {};
userData.firstName = "dd";
userData.lastName = false;
userData.email = 0;
userData.phone = 123;
userData.address = "aad";
userData.mailAddress = "dff";
const detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
const detailsConditionsArr2 = [true, 1, "fff"];
const doesHaveFalsy = obj => obj.some(x => !x);
if (doesHaveFalsy(detailsConditionsArr)) {
console.log("detailsConditionsArr Found a falsy");
}
if (doesHaveFalsy(detailsConditionsArr2)) {
console.log("detailsConditionsArr2 Found a falsy");
}
More about some - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some

Javascript - Get object fields using a given array of keys

Given this object:
const userData = {
avatar: undefined,
name: "Raul",
username: "raulito",
celebrity: true
}
I need to implement a method that receives a list of keys (i.e., ["avatar", "name", "username"]) and get the corresponding values of the userData object, ignoring undefined values.
How can I do this using modern javascript syntax?
function denormalizeUserData(userData, ...fields) {
const denormalized = {};
// For each key (field), get its value from userData, ignoring if undefined
return denormalized;
}
So, if I do:
denormalizeUserData(userData, "avatar", "name");
The method must return me:
{
name: "Raul,
}
Ignoring avatar, as it is undefined.
This is my attempt. I need modern syntax.
const userData = {
avatar: undefined,
name: "Raul",
username: "raulito",
celebrity: true
}
function denormalizeUserData(userData, ...fields) {
const denormalized = {};
fields.forEach((key) => {
const value = userData[key];
if(typeof value !== "undefined") {
denormalized[key] = value;
}
})
return denormalized;
}
console.log(denormalizeUserData(userData, "celebrity", "name", "avatar"))
function denormalizeUserData(userData, ...fields) {
const denormalized = {};
for (const field of fields) {
if (userData[field] !== undefined) denormalized[field] = userData[field];
}
return denormalized;
}
Edit: in case someone says a code-only answer is blah blah blah
This is simple enough to be a simple code block.
You can get the object property/value pairs with Object.entries, then use Array.filter to filter out the pairs whose property name is not included in fields and whose value is undefined, then use Object.fromEntries to convert it back to an object.
const userData = {
avatar: undefined,
name: "Raul",
username: "raulito",
celebrity: true
}
function denormalizeUserData(userData, ...fields) {
return Object.fromEntries(Object.entries(userData).filter(e => fields.includes(e[0]) && e[1] != undefined))
}
console.log(denormalizeUserData(userData, "avatar", "name"))
Grab the Object.entries and build a new object if there is a value, and if the args array includes the key.
const userData = {
avatar: undefined,
name: "Raul",
username: "raulito",
celebrity: true
};
function denormalizeUserData(obj, ...args) {
const out = {};
for (let [key, value] of Object.entries(obj)) {
if (value && args.includes(key)) {
out[key] = value;
};
}
return out;
}
console.log(denormalizeUserData(userData));
console.log(denormalizeUserData(userData, 'avatar', 'name'));
console.log(denormalizeUserData(userData, 'avatar', 'name', 'celebrity'));

React extracting a nested json object

How can I extract the 'jobs' object from a nested json list like this:
result:
{
person:
[
{
name: ""
address: ""
jobs: [
{
company:""
},
{
company:""
}
]
}
]
}
Thank you
Write a generic method to extract object properties.
function onExtract(key, data) {
if (isObject(data)) {
for (let item in data) {
if (key === item) {
return data[item];
}
const res = onExtract(key, data[item]);
if (res !== null) return res;
}
}
if (isArray(data)) {
for (let item of data) {
const res = onExtract(key, item);
if (res !== null) return res;
}
}
return null;
}
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === "[object Array]";
}
// test
const data = {
person: [
{
name: "",
address: "",
jobs: [
{
company: ""
},
{
company: ""
}
]
}
]
};
console.log(onExtract("jobs", data));
let's say you have a return var that contains this json value
let mappedCompanies = return.person.map(person =>
person.jobs.map(job => job.company)
).flatMap(m => m)
mappedCompanies would contain an array with all the companies names for each one of the registers in "person", all as one array of strings
you can read more about Array.map() here: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/map
A dynamic way to query the person[] and find jobs, is to use the javascript map() method.
Here is the code without comments.
const personsJobs = (personName, personAddress) => {
const jobs = result.person.map((el) => {
if (el.name === personName && el.address === personAddress) {
return el.jobs;
} else {
return null;
}
})
.filter((el) => el !== null);
return jobs;
};
console.log(personsJobs("wyatt", "1234 test ln"));
Here is the code with comments to explain how the personsJob function works.
// Blow is an ES6 arrow function with the parameters 'personName' and 'personAddress',
// which represents the person in which you are querying for jobs (using both a persons
// name and address so in the case of persons with the same name, you only find the jobs
// of the person you want).
const personsJobs = (personName, personAddress) => {
// Since 'person' is an array, we can use the 'map' method as stated before, which
// will create a new array (jobs) that will store the jobs a specific person has.
const jobs = result.person.map((el) => {
// el stands for the current position in the person array.
// if el's (the current person) name and address values are equal to that of the
// parameters personName and personAddress, then that persons jobs are added to the jobs // array, however, if el does not satisfy the two parameters, null is added to the jobs
// array.
// The array, upon completion, will look something like this: ["programmer", null, null]
if (el.name === personName && el.address === personAddress) {
return el.jobs;
} else {
return null;
}
})
// Finally, the filter method is called to remove all null values so that you will
// only have the persons job in the jobs array.
// After filtering, the array will look like this: ["programmer"]
.filter((el) => el !== null);
return jobs;
};
// Prints the array of wyatt's jobs
console.log(personsJobs("wyatt", "1234 test ln"));
So, following the conclusion of the function, you will have dynamically found the jobs of a specific person.
you can use flatMap function like:
const jobsData = result.person.flatMap(item => item.jobs);
Here is a flexible solution using object-scan
// const objectScan = require('object-scan');
const data = { person: [{ name: '', address: '', jobs: [{ company: '' }, { company: '' }] }] };
console.log(objectScan(['person[*].jobs'], { reverse: false, rtn: 'value' })(data));
// => [ [ { company: '' }, { company: '' } ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan

Update fields in nested objects in Typescript / Javascript

In Firestore you can update fields in nested objects by a dot notation (https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). I wonder how to make that work in Typescript / Javascript.
For example the following object:
const user = {
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: true
}
},
token: {
custom: 'safghhattgaggsa',
public: 'fsavvsadgga'
}
}
How can I update this object with the following changes:
details.email.verified = false;
token.custom = 'kka';
I already found that Lodash has a set function:
_.set(user, 'details.email.verified', false);
Disadvantage: I have to do this for every change. Is their already a method to update the object with an object (like firestore did)?
const newUser = ANYFUNCTION(user, {
'details.email.verified': false,
'token.custom' = 'kka'
});
// OUTPUT for newUser would be
{
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: false
}
},
token: {
custom: 'kka',
public: 'fsavvsadgga'
}
}
Does anyone know an good solution for this? I already found more solutions if I only want to change one field (Dynamically set property of nested object), but no solution for more than one field with one method
I think you are stuck with using a function but you could write it yourself. No need for a lib:
function set(obj, path, value) {
let parts = path.split(".");
let last = parts.pop();
let lastObj = parts.reduce((acc, cur) => acc[cur], obj);
lastObj[last] = value;
}
set(user, 'details.email.verified', false);
if what you want to do is merge 2 objects then it is a bit trickier:
function forEach(target, fn) {
const keys = Object.keys(target);
let i = -1;
while (++i < keys.length) {
fn(target[keys[i]], keys[i]);
}
}
function setValues(obj, src) {
forEach(src, (value, key) => {
if (value !== null && typeof (value) === "object") {
setValues(obj[key], value);
} else {
obj[key] = value;
}
});
}
let obj1 = {foo: {bar: 1, boo: {zot: null}}};
let obj2 = {foo: {baz: 3, boo: {zot: 5}}};
setValues(obj1, obj2);
console.log(JSON.stringify(obj1));
One solution in combination with lodash _.set method could be:
function setObject(obj, paths) {
for (const p of Object.keys(paths)) {
obj = _.set(obj, p, paths[p]);
}
return obj;
}

Categories

Resources