Remove underscore prefixes from all keys - javascript

I have data that looks like the following:
[
{
"_sourceAddresses":[
{
"_street1":"957 Heathcote Unions",
"_city":"Matteoside",
"_state":"Hawaii",
"_postalCode":"69680",
"_postalCodePlusFour":"7715",
"_country":"USA",
"_type":0,
"_updatedAt":"1991-03-10T22:34:27.000Z",
"_createdAt":"1970-07-24T09:34:12.000Z"
}
],
"_emails":[
{
"_address":"labadie.gwendolyn#gmail.com",
"_primary":true
}
],
"_phoneNumbers":[
{
"_number":"4612902836",
"_type":0,
"_carrier":"AT&T"
}
],
"_customFields":{
},
"_active":true,
"_firstName":"Haven",
"_lastName":"Runolfsdottir",
"_gender":"M",
"_sourceIndividualId":"c1126d05-0e5b-4da1-8535-e1061d4163ee",
"_sourceCampusId":"ae1e70d5-d8bf-4942-b9ea-3da5765e055f",
"_primaryContact":true,
"_salutation":"Mrs.",
"_suffix":"DDS",
"_birthDate":"1989-02-16T10:06:25.000Z"
},
{
"_sourceAddresses":[
{
"_street1":"5910 Langosh Burgs Apt. 281",
"_city":"West Katheryn",
"_state":"Arkansas",
"_postalCode":"49571",
"_postalCodePlusFour":null,
"_country":"USA",
"_type":0,
"_updatedAt":"1984-01-09T09:34:02.000Z",
"_createdAt":"1986-01-13T17:36:41.000Z"
}
],
"_emails":[
{
"_address":"labadie_cristopher#yahoo.com",
"_primary":true
}
],
"_phoneNumbers":[
{
"_number":"0608405498",
"_type":0,
"_carrier":"Verizon"
}
],
"_customFields":{
},
"_active":true,
"_firstName":"Andreane",
"_lastName":"Kerluke",
"_gender":"F",
"_sourceIndividualId":"0726bfc2-56af-4e46-90ef-c0a286404334",
"_sourceCampusId":"86fdb656-7e29-4ace-a1c7-149db81c7f5e",
"_primaryContact":true,
"_salutation":"Mrs.",
"_suffix":null,
"_birthDate":"1979-11-14T10:07:02.000Z"
}
]
When it is saved as JSON, I'd like to remove the underscores in the keys. Is there an easy way to do this?
I have tried unsuccessfully to adapt this code to accomplish it:
Replace dot to underscore in js object keys names
function removeLeadingUnderscores(obj) {
_.forOwn(obj, (value, key) => {
if (_.startsWith("_")) {
const cleanKey = _.substring(1)
obj[cleanKey] = value;
delete obj[key];
}
// continue recursively looping through if we have an object or array
if (_.isObject(value)) {
return removeLeadingUnderscores(value);
}
});
return obj;
}

Since you're planning to save as JSON already, you can use its naturally recursive nature with its reviver parameter to return objects without the underscores. Map the entries of the object to a new object without the leading _.
const arr=[{_sourceAddresses:[{_street1:"957 Heathcote Unions",_city:"Matteoside",_state:"Hawaii",_postalCode:"69680",_postalCodePlusFour:"7715",_country:"USA",_type:0,_updatedAt:"1991-03-10T22:34:27.000Z",_createdAt:"1970-07-24T09:34:12.000Z"}],_emails:[{_address:"labadie.gwendolyn#gmail.com",_primary:!0}],_phoneNumbers:[{_number:"4612902836",_type:0,_carrier:"AT&T"}],_customFields:{},_active:!0,_firstName:"Haven",_lastName:"Runolfsdottir",_gender:"M",_sourceIndividualId:"c1126d05-0e5b-4da1-8535-e1061d4163ee",_sourceCampusId:"ae1e70d5-d8bf-4942-b9ea-3da5765e055f",_primaryContact:!0,_salutation:"Mrs.",_suffix:"DDS",_birthDate:"1989-02-16T10:06:25.000Z"},{_sourceAddresses:[{_street1:"5910 Langosh Burgs Apt. 281",_city:"West Katheryn",_state:"Arkansas",_postalCode:"49571",_postalCodePlusFour:null,_country:"USA",_type:0,_updatedAt:"1984-01-09T09:34:02.000Z",_createdAt:"1986-01-13T17:36:41.000Z"}],_emails:[{_address:"labadie_cristopher#yahoo.com",_primary:!0}],_phoneNumbers:[{_number:"0608405498",_type:0,_carrier:"Verizon"}],_customFields:{},_active:!0,_firstName:"Andreane",_lastName:"Kerluke",_gender:"F",_sourceIndividualId:"0726bfc2-56af-4e46-90ef-c0a286404334",_sourceCampusId:"86fdb656-7e29-4ace-a1c7-149db81c7f5e",_primaryContact:!0,_salutation:"Mrs.",_suffix:null,_birthDate:"1979-11-14T10:07:02.000Z"}];
const stringified = JSON.stringify(
arr,
(_, value) => {
return value && typeof value === 'object' && !Array.isArray(value)
? Object.fromEntries(
Object.entries(value)
.map(([key, value]) => [key.slice(1), value])
)
: value;
}
);
console.log(stringified);
If some properties don't start with _, you can change .slice(1) to .replace(/^_/, '').

Here's a simplified version of saving the object with removed underscores through simple recursive logic:
let savedJson: any = [];
renamingArray(obj); // obj is your object
function renamingArray(element: any){
for(let element of obj)
if (Object.prototype.toString.call(element) === '[object Array]') {
renamingArray(element);
else
renamingObject(element);
}
}
function renamingObject(obj: any){
let keys = Object.keys(obj)
for(let objectKey of keys){
savedJson.push({ [objectKey.substring(1)]: obj[objectKey] });
}
}
console.log(savedJson)

Related

Search function that iterates through an array and returns the values matched init also in the child object

I'm trying to search an array of objects with objects that are nested in, so for example i have this array:
[
{
website: 'Stackoverflow',
info: {
"extension": "com",
"ssl": true
}
},
{
website: 'Faceoobok',
info: {
"extension": "com",
"ssl": true
}
}
]
So I want to search all fields, and then also search the object inside and return an array with the method filter, also the char cases won't matter, it needs to return the object in the array even for example Stackoverflow is not the same as stackoverflow with the casing methods that come with JS.
Here is what I've tried, and It searches the objects and returns them but It doesn't search the object inside, what I mean is for example it searchs the website, but not the .info:
const searchMachine = (arr, query) => {
let queryFormatted = query.toLowerCase();
return arr.filter((obj) =>
Object.keys(obj).some((key) => {
if (typeof obj[key] === 'string') {
return obj[key]
.toLowerCase()
.includes(queryFormatted);
}
return false;
})
);
You could take a closure over the wanted string and use a recursive approach for objects.
const
searchMachine = (array, query) => {
const
check = (query => object => Object
.values(object)
.some(value =>
typeof value === 'string' && value.toLowerCase().includes(query) ||
value && typeof value === 'object' && check(value)
))(query.toLowerCase());
return array.filter(check);
},
data = [{ website: 'Stackoverflow', info: { extension: 'com', ssl: true } }, { website: 'Faceoobok', info: { extension: 'com', ssl: true } }];
console.log(searchMachine(data, 'stack'));
console.log(searchMachine(data, 'com'));
You can split the task in two step. The first one is to get all string in the object.
function getAllStrings(obj) {
if(typeof obj === 'object'){
return Object.values(obj).flatMap(v => getAllStrings(v));
}else if (typeof obj === 'string'){
return [obj];
}
return [];
}
And the second one is to filter.
const searchMachine = (arr, query) => {
const queryFormatted= query.toLowerCase();
return getAllStrings(arr).filter(s => s.toLowerCase().includes(queryFormatted));
}
You can reuse the Object.keys.some(...) code you used to search in the object, to search in object.info.
First make a function of it that lets us pass in the object:
const findInObject = (obj) =>
Object.keys(obj).some((key) => {
if (typeof obj[key] === 'string') {
return obj[key]
.toLowerCase()
.includes(queryFormatted);
}
return false;
});
Then call it within arr.filter. findInObject(obj) is your original logic, and check for the presence of obj.info and then call findInObject on obj.info
...
return arr.filter((obj) =>
findInObject(obj) || obj.info && findInObject(obj.info)
);
...

Adding dynamic properties to object only if the name is defined

i have a function like this:
const getKeysAs = (key1, key2) => {
return {
[key1]: state.key1,
[key2]: state.key2
}
}
So if state.key1 is 'a' and state.key2 is 'b', calling getKyesAs('one', 'two') would return
{
one: 'a',
two: 'b'
}
Now, if one of the argument is undefined, is there a way to not include it in the returned object ?
You can Conditionally add properties to an Object with object destructuring
const obj = {
...(key1 && { [key1]: state[key1] }),
...(key2 && { [key2]: state[key2] })
};
If some of the args function is undefined, null or 0 (falsy values) then it will no be added to the object.
There is a very scalable way to do it:
const state= {
a: "hello",
}
function getKeysAs (keys) {
return [...arguments].reduce((acc, cur)=> {
const newValue = state[cur] && {[cur]: state[cur]}
return {...acc, ...newValue}
}, {})
}
console.log(getKeysAs("a", "b"))
This way, you can pass as much keys as you need without worrying about scalability & undefined values.
Use Object.assign().
const getKeysAs = (key1, key2) => {
return Object.assign({}, key1 && {[key1]: state[key1]}, key2 && {[key2]: state[key2]});
}
Assuming you actually mean to do state[key1], not state.key1, here is a solution that doesn't create superfluous objects:
const getKeysAs = (...keys) => {
const result = {};
for (const key of keys) {
if (key != null) {
result[key] = state[key];
}
}
return result;
}

Get nested objects key as joined string

Input:
{
"mobile": "Mob # Required",
"client": [
undefined,
null,
{
"usergroup": "Required"
},
{
"id": "Required",
"usergroup": "Required"
},
{
"id": "Required",
"usergroup": "Required"
}
]
}
Expected Output:
[
"mobile",
"client.2.usergroup",
"client.3.id",
"client.3.usergroup",
"client.4.id",
"client.4.usergroup"
]
I am using Formiks FieldArray in my project & the field name in error object is not what is expected.
Object.Keys() doesn't work well for such scenario.
You can flatMap the keys of the object. If the current key's value is an object, recursively call getKeys function with the updated prefix. If not, return the current key with the given provided prefix. Use flatMap to get a flattened array of keys instead of nested arrays
const input={mobile:"Mob # Required",client:[{usergroup:"Required"},{id:"Required",usergroup:"Required"},{id:"Required",usergroup:"Required"}]};
const getKeys = (o, prefix = '') =>
Object.keys(o).flatMap(k =>
Object(o[k]) === o[k] ? getKeys(o[k], `${prefix}${k}.`) : [prefix + k]
)
console.log(getKeys(input))
If flatMap is not supported, you can reduce the keys of the object with similar logic
const input={mobile:"Mob # Required",client:[{usergroup:"Required"},{id:"Required",usergroup:"Required"},{id:"Required",usergroup:"Required"}]};
function getKeys(o, prefix = '') {
return Object.keys(o).reduce((acc, k) => {
if (Object(o[k]) === o[k])
acc.push(...getKeys(o[k], `${prefix}${k}.`))
else
acc.push(prefix + k)
return acc;
}, [])
}
console.log(getKeys(input))
I'm sure there are libraries out there to do this for you, but here is what I came up with:
function flattenNestedObject(input, path) {
path = path || [];
return Object.keys(input).reduce(function(arr, key) {
if (input[key] && typeof input[key] === "object") {
return arr.concat(flattenNestedObject(input[key], path.concat(key)));
}
if (typeof input[key] !== 'undefined' && input[key] !== null) {
return arr.concat((path.concat(key).join(".")));
}
return arr;
}, []);
}
https://codesandbox.io/s/sweet-jang-j51dh
The reason Object.keys will not work for you is that it doesn't recursively obtain all keys of an object. Also, in your expected output, you don't want all the keys of an object if it contains nested Arrays or Objects.

how to get keys of nested object

If I have a flat object then this works:
let stateCopy={...this.state}
Object.entries(dictionary).map(([key,value])=>{
stateCopy.key = value.toString())
})
Is there a way to do this if dictionary contains a nested object. Suppose a dictionary looks like:
dictionary={left:{name:'WORK',
min:2,
sec:0,}
start:true}
I need some way of updating stateCopy, i.e
stateCopy.left.name='WORK'
stateCopy.left.min=2
stateCopy.left.sec=0
stateCopy.start=true
function flattenDictionary(dict) {
if (!dict) {
return {};
}
/** This will hold the flattened keys/values */
const keys = {};
// Perform the flatten
flattenH(dict);
return keys;
function flattenH(obj, prefix) {
Object.keys(obj).forEach((key) => {
const val = obj[key];
/** This is what we pass forward as a new prefix, or is the flattened key */
let passKey;
// Only expect to see this when the original dictionary is passed as `obj`
if (!prefix || prefix === '') {
passKey = key;
} else {
// "Ignore" keys that are empty strings
passKey = ((key === '') ? prefix : `${prefix}.${key}`);
}
if (typeof obj[key] !== 'object') {
keys[passKey] = val;
} else {
flattenH(val, passKey);
}
});
}
}
Seems like you can do this with a little recursive function:
let state = {
left:{
start: "mark",
anotherLevel: {
test: 'leveltest'
}
},
test: "will be replaced"
}
let dictionary={
test2: {
foo: 'bar'
},
left:{
name:'WORK',
min:2,
sec:0,
anotherLevel: {
test_add: 'leveltest_add'
}
},
start:true,
test: 'replaced with me'
}
let stateCopy={...state}
function merge(obj, dict){
Object.entries(dict).forEach(([k, v]) =>{
if (!obj[k] || typeof v !== 'object') obj[k] = v
else merge(obj[k], v)
})
}
merge(stateCopy, dictionary)
console.log(stateCopy)

How to deeply remove keys in object?

I have this json object returned from an API that has a few quirks, and I'd like to normalize it so I can process the input the same for every response. These means getting rid of superfluous keys:
Response:
{
_links: {...},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
}
]
}
}
So I'd like to remove all the _embedded keys and flatten it, like so:
{
_links: {...},
foo: [
{
id: 2,
bar: []
}
]
}
This is what I have at the moment, but it only works for the top level and I don't think it'll play well with arrays.
_.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
}
return accumulator[key] = value;
}, {})
Loop in recursion on all of your keys, once you see a key which start with _
simply remove it.
Code:
var
// The keys we want to remove from the Object
KEYS_TO_REMOVE = ['_embedded'],
// The data which we will use
data = {
_links: {'a': 1},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
},
{
id: 3,
_embedded: {
bar: [
{
id: 4,
_embedded: {
bar: []
}
}
]
}
}
]
}
};
/**
* Flatten the given object and remove the desired keys if needed
* #param obj
*/
function flattenObject(obj, flattenObj) {
var key;
// Check to see if we have flatten obj or not
flattenObj = flattenObj || {};
// Loop over all the object keys and process them
for (key in obj) {
// Check that we are running on the object key
if (obj.hasOwnProperty(key)) {
// Check to see if the current key is in the "black" list or not
if (KEYS_TO_REMOVE.indexOf(key) === -1) {
// Process the inner object without this key
flattenObj[key] = flattenObject(obj[key], flattenObj[key]);
} else {
flattenObject(obj[key], flattenObj);
}
}
}
return flattenObj;
}
console.log(flattenObject(data));
So, basically you already have almost all of the code you need. All we have to do is wrap it in a function so we can use recursion. You'll see we only add a check to see if it is an object, if it is, we already have a function that knows how to flatten that object, so we'll just call it again with the key that we need to flatten.
function flatten(temp1) { // Wrap in a function so we can use recursion
return _.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
} else if (value !== null && typeof value === 'object') // Check if it's another object
return _.merge(accumulator, flatten(value)) // Call our function again
return accumulator[key] = value;
}, {})
}
I'll be able to test it in a bit, but this should be what you need.
Got it!
function unEmbed(data) {
return _.reduce(data, function(accumulator, value, key) {
const returnableValue = _.isObject(value) ? unEmbed(value) : value;
if (key === 'embedded') {
return _.merge(accumulator, returnableValue);
}
accumulator[key] = returnableValue;
return accumulator;
}, {});
}
Problem before I was returning return accumulator[key] = returnableValue, which worked out to be return returnableValue.

Categories

Resources