check the existence of a key in associative arrays - javascript

Tell me, how correctly to check the existence of a key in associative arrays?
For example:
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: ''
value2" '',
},
},
subkey2: '';
},
}
if ((mydata.key2 != undefined) && (mydata.key2.subkey1 != undefined) && (mydata.key2.subkey1.subkey1_1 != undefined))
mydata.key2.subkey1.subkey1_1.value1 = 'test';
Too long and confusing
((mydata.key2 != undefined) && (mydata.key2.subkey1 != undefined) && (mydata.key2.subkey1.subkey1_1 != undefined))
I would like to use a simpler function, like
safeSet(mydata.key2.subkey1.subkey1_1.value1, 'test');
or
if (is_undefined(mydata.key2.subkey1.subkey1_1.value1) == true)
mydata.key2.subkey1.subkey1_1.value1 = 'test'; // now - error if 'mydata.key2.subkey1.subkey1_1' not exist

You can create custom function using reduce() to test if nested property exists. You can just pass key as string.
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: '',
value2: '',
},
},
subkey2: ''
},
}
function safeSet(key, data) {
return key.split('.').reduce(function(r, e) {
return r ? r[e] : undefined;
}, data) != undefined
}
console.log(safeSet('key2.subkey1.subkey1_1.value1', mydata))

You should use the in operator:
"key" in obj // true, regardless of the actual value
Or, if you want to particularly test for properties of the object instance (and not inherited properties), use hasOwnProperty:
obj.hasOwnProperty("key") // true
hope this would help you.
Source: http://www.advancesharp.com/questions/628/checking-if-an-associative-array-key-exists-in-javascript

Alternatively, you can make use of the .has() method of Lodash.
Then, you would only need to check:
if (_.has(mydata, 'key2.subkey1.subkey1_1.value1')
mydata.key2.subkey1.subkey1_1.value1 = 'test';

For trying to get something in a nested structure I'd do something like this:
function getPath(element, path) {
var handledSoFar = [];
for (var i = 0; i < path.length; i++) {
var property = path[i];
handledSoFar.push(property);
if (typeof element[property] === 'undefined') {
throw new Error('Path ' + handledSoFar.join('->') + ' is undefined');
}
element = object[property];
}
return element;
}
var mydata = {
key1: '',
key2: {
subkey1: {
subkey1_1: {
value1: '',
value2: 'hi'
}
},
subkey2: ''
}
};
// Prints 'hi'
console.log(getPath(mydata, ['key2', 'subkey1', 'subkey1_1', 'value2']));
// Throws error 'Path key2->subkey2->subkey1_1 is undefined'
console.log(getPath(mydata, ['key2', 'subkey1', 'subkey1_1', 'value2']));
Of course keeping track of the search in handledSoFar is optional but might be useful for development / debugging.

You can also use the lodash deep field selector: lodash.get (documentation)
const get = require('lodash.get');
const set = require('lodash.set');
if (!get(mydata, 'key2.subkey1.subkey1_1.value1')) {
set(mydata, 'key2.subkey1.subkey1_1.value1', 'test');
}

You could split the path and make a check if the following element exist. If not assign an object to the new property.
Return then the value of the property.
At the end assign the value.
function setValue(object, path, value) {
var fullPath = path.split('.'),
way = fullPath.slice(),
last = way.pop();
way.reduce(function (r, a) {
return r[a] = r[a] || {};
}, object)[last] = value;
}
var object = { key1: '', key2: { subkey1: { subkey1_1: { value1: '', value2: '' } }, subkey2: '' } };
setValue(object, 'key2.subkey1.subkey1_1.value1', 'test');
console.log(object);

The problem with the example function that you proposed:
safeSet(mydata.key2.subkey1.subkey1_1.value1, 'test');
or
is_undefined(mydata.key2.subkey1.subkey1_1.value1)
Is that the mydata.key2.subkey1... part is run before the function is called. So if one of the subkeys does not exist, an exception will be thrown before your code is reached.
You could get something similar using a callback though...
safeSet(function(val) { mydata.key2.subkey1.subkey1_1.value1 = val; }, 'test')
the implementation of safeSet would then be:
var safeSet = function(setterFunc, val) {
try {
setterFunc(val);
} catch (e) {
if (e instanceof TypeError) {
return false;
} else {
throw e;
}
}
return true;
}
safeSet returns true if the value was set, and false otherwise.

Related

How to validate deeply nested object structure

I have defined object with nested properties. I want to create a validator function which will check if another object has the same structure and value type as the one that I have defined!
The is the definition of the object:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
mSummary: String,
mMarkets: Array,
mBdd: String,
mReplaceDict: Object,
omId: String,
omnSummary: String,
omnMarkets: Array,
omnBdd: String,
omnReplaceDict: {
id: String,
text: String,
},
}],
metadata: {
emails: Array,
description: String,
},
};
And here is the function that I have for validation. Currently it works only with one nested level! I want it to validate with many nested levels.
function validateObjectStructure(schema, obj) {
let valid = true;
firstLevel: for(const k in schema) {
if(schema[k].constructor === Array) { // if prop is of type array
let i;
for(i = 0; i < schema[k].length; i++) {
for(const kk in schema[k][i]) {
if(!obj[k][i].hasOwnProperty(kk) || obj[k][i][kk].constructor !== schema[k][i][kk]) {
valid = false;
break firstLevel;
}
}
}
}
else if(schema[k].constructor === Object) { // if prop is of type object
for(const kk in schema[k]) {
if(!obj[k].hasOwnProperty(kk) || obj[k][kk].constructor !== schema[k][kk]) {
valid = false;
break firstLevel;
}
}
}
else { // if prop is simple type
if(!obj.hasOwnProperty(k) || obj[k].constructor !== schema[k]) {
valid = false;
break;
}
}
}
return valid;
}
Do you need to work with nested levels of the obj? If yes, you can do something like this instead of the last line:
Object.values(obj).reduce((accValid, value) => {
if (typeof value === 'object') {
return accValid && validateObjectStructure(schema, value);
}
return accValid;
}, valid);
return valid;
Here's a possible implementation:
function validate(obj, schema, path = '') {
let ok = true;
if (!obj)
ok = obj === schema;
else if (typeof schema === 'function')
ok = obj.constructor === schema;
else if (typeof obj !== 'object')
ok = obj === schema;
else if (Array.isArray(schema))
ok = Array.isArray(obj) && obj.every((x, k) => validate(x, schema[0], path + '[' + k + ']'));
else {
let ko = Object.keys(obj);
let ks = Object.keys(schema);
ok = ko.length === ks.length && ks.every(k => validate(obj[k], schema[k], path + '.' + k));
}
if (!ok)
throw new Error('FAILED ' + path);
return true;
}
// example:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
omnReplaceDict: {
id: String,
text: {
deepObj: {
deepProp: [Number]
}
},
},
}],
};
const obj = {
name: "foo",
data: [{
isSelected: true,
mId: "bar",
omnReplaceDict: {
id: "foo",
text: {
deepObj: {
deepProp: [1, 2, "???", 3]
}
},
},
}]
};
validate(obj, OBJECT_SCHEMA)
Note: although this home-made type checker appears to work correctly, it's quite limited (e.g. how to express "array of string-number pairs" or "either null or some object"?), so it might be an option to employ a real one, like Typescript. See here for a possible implementation.

How to implement a recursive function similar to Array.prototype.find [duplicate]

Let's say I have an object:
[
{
'title': "some title"
'channel_id':'123we'
'options': [
{
'channel_id':'abc'
'image':'http://asdasd.com/all-inclusive-block-img.jpg'
'title':'All-Inclusive'
'options':[
{
'channel_id':'dsa2'
'title':'Some Recommends'
'options':[
{
'image':'http://www.asdasd.com' 'title':'Sandals'
'id':'1'
'content':{
...
I want to find the one object where the id is 1. Is there a function for something like this? I could use Underscore's _.filter method, but I would have to start at the top and filter down.
Recursion is your friend. I updated the function to account for property arrays:
function getObject(theObject) {
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = getObject(theObject[i]);
if (result) {
break;
}
}
}
else
{
for(var prop in theObject) {
console.log(prop + ': ' + theObject[prop]);
if(prop == 'id') {
if(theObject[prop] == 1) {
return theObject;
}
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
result = getObject(theObject[prop]);
if (result) {
break;
}
}
}
}
return result;
}
updated jsFiddle: http://jsfiddle.net/FM3qu/7/
Another (somewhat silly) option is to exploit the naturally recursive nature of JSON.stringify, and pass it a replacer function which runs on each nested object during the stringification process:
const input = [{
'title': "some title",
'channel_id': '123we',
'options': [{
'channel_id': 'abc',
'image': 'http://asdasd.com/all-inclusive-block-img.jpg',
'title': 'All-Inclusive',
'options': [{
'channel_id': 'dsa2',
'title': 'Some Recommends',
'options': [{
'image': 'http://www.asdasd.com',
'title': 'Sandals',
'id': '1',
'content': {}
}]
}]
}]
}];
console.log(findNestedObj(input, 'id', '1'));
function findNestedObj(entireObj, keyToFind, valToFind) {
let foundObj;
JSON.stringify(entireObj, (_, nestedValue) => {
if (nestedValue && nestedValue[keyToFind] === valToFind) {
foundObj = nestedValue;
}
return nestedValue;
});
return foundObj;
};
What worked for me was this lazy approach, not algorithmically lazy ;)
if( JSON.stringify(object_name).indexOf("key_name") > -1 ) {
console.log("Key Found");
}
else{
console.log("Key not Found");
}
If you want to get the first element whose id is 1 while object is being searched, you can use this function:
function customFilter(object){
if(object.hasOwnProperty('id') && object["id"] == 1)
return object;
for(var i=0; i<Object.keys(object).length; i++){
if(typeof object[Object.keys(object)[i]] == "object"){
var o = customFilter(object[Object.keys(object)[i]]);
if(o != null)
return o;
}
}
return null;
}
If you want to get all elements whose id is 1, then (all elements whose id is 1 are stored in result as you see):
function customFilter(object, result){
if(object.hasOwnProperty('id') && object.id == 1)
result.push(object);
for(var i=0; i<Object.keys(object).length; i++){
if(typeof object[Object.keys(object)[i]] == "object"){
customFilter(object[Object.keys(object)[i]], result);
}
}
}
Improved #haitaka answer, using the key and predicate
function deepSearch (object, key, predicate) {
if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object
for (let i = 0; i < Object.keys(object).length; i++) {
let value = object[Object.keys(object)[i]];
if (typeof value === "object" && value != null) {
let o = deepSearch(object[Object.keys(object)[i]], key, predicate)
if (o != null) return o
}
}
return null
}
So this can be invoked as:
var result = deepSearch(myObject, 'id', (k, v) => v === 1);
or
var result = deepSearch(myObject, 'title', (k, v) => v === 'Some Recommends');
Here is the demo: http://jsfiddle.net/a21dx6c0/
EDITED
In the same way you can find more than one object
function deepSearchItems(object, key, predicate) {
let ret = [];
if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) {
ret = [...ret, object];
}
if (Object.keys(object).length) {
for (let i = 0; i < Object.keys(object).length; i++) {
let value = object[Object.keys(object)[i]];
if (typeof value === "object" && value != null) {
let o = this.deepSearchItems(object[Object.keys(object)[i]], key, predicate);
if (o != null && o instanceof Array) {
ret = [...ret, ...o];
}
}
}
}
return ret;
}
If you're into the whole ES6 thing you can use
const findByKey = (obj, kee) => {
if (kee in obj) return obj[kee];
for(n of Object.values(obj).filter(Boolean).filter(v => typeof v === 'object')) {
let found = findByKey(n, kee)
if (found) return found
}
}
const findByProperty = (obj, predicate) => {
if (predicate(obj)) return obj
for(n of Object.values(obj).filter(Boolean).filter(v => typeof v === 'object')) {
let found = findByProperty(n, predicate)
if (found) return found
}
}
find by value is going to be a little different
let findByValue = (o, val) => {
if (o === val) return o;
if (o === NaN || o === Infinity || !o || typeof o !== 'object') return;
if (Object.values(o).includes(val)) return o;
for (n of Object.values(o)) {
const found = findByValue(n, val)
if (found) return n
}
}
then they can be used like this
const arry = [{ foo: 0 }, null, { bar: [{ baz: { nutherKey: undefined, needle: "gotcha!" } }]}]
const obj = { alice: Infinity, bob: NaN, charlie: "string", david: true, ebert: arry }
findByKey(obj, 'needle')
// 'gotcha!'
findByProperty(obj, val => val.needle === 'gotcha!')
// { nutherKey: undefined, needle: "gotcha!" }
findByValue(obj, 'gotcha!')
// { nutherKey: undefined, needle: "gotcha!" }
I found this page through googling for the similar functionalities. Based on the work provided by Zach and regularmike, I created another version which suits my needs.
BTW, teriffic work Zah and regularmike!
I'll post the code here:
function findObjects(obj, targetProp, targetValue, finalResults) {
function getObject(theObject) {
let result = null;
if (theObject instanceof Array) {
for (let i = 0; i < theObject.length; i++) {
getObject(theObject[i]);
}
}
else {
for (let prop in theObject) {
if(theObject.hasOwnProperty(prop)){
console.log(prop + ': ' + theObject[prop]);
if (prop === targetProp) {
console.log('--found id');
if (theObject[prop] === targetValue) {
console.log('----found porop', prop, ', ', theObject[prop]);
finalResults.push(theObject);
}
}
if (theObject[prop] instanceof Object || theObject[prop] instanceof Array){
getObject(theObject[prop]);
}
}
}
}
}
getObject(obj);
}
What it does is it find any object inside of obj with property name and value matching to targetProp and targetValue and will push it to the finalResults array.
And Here's the jsfiddle to play around:
https://jsfiddle.net/alexQch/5u6q2ybc/
I've created library for this purpose: https://github.com/dominik791/obj-traverse
You can use findFirst() method like this:
var foundObject = findFirst(rootObject, 'options', { 'id': '1' });
And now foundObject variable stores a reference to the object that you're looking for.
Another recursive solution, that works for arrays/lists and objects, or a mixture of both:
function deepSearchByKey(object, originalKey, matches = []) {
if(object != null) {
if(Array.isArray(object)) {
for(let arrayItem of object) {
deepSearchByKey(arrayItem, originalKey, matches);
}
} else if(typeof object == 'object') {
for(let key of Object.keys(object)) {
if(key == originalKey) {
matches.push(object);
} else {
deepSearchByKey(object[key], originalKey, matches);
}
}
}
}
return matches;
}
usage:
let result = deepSearchByKey(arrayOrObject, 'key'); // returns an array with the objects containing the key
You can use javascript some function inside a recursive function. The advantage of some is to stop looping once the child is founded. Do not use map that would be slow in large data.
const findChild = (array, id) => {
let result;
array.some(
(child) =>
(child.id === id && (result = child)) ||
(result = findChild(child.options || [], id))
);
return result;
};
findChild(array, 1)
Just use recursive function.
See example below:
const data = [
{
title: 'some title',
channel_id: '123we',
options: [
{
channel_id: 'abc',
image: 'http://asdasd.com/all-inclusive-block-img.jpg',
title: 'All-Inclusive',
options: [
{
channel_id: 'dsa2',
title: 'Some Recommends',
options: [
{
image: 'http://www.asdasd.com',
title: 'Sandals',
id: '1',
content: {},
}
]
}
]
}
]
}
]
function _find(collection, key, value) {
for (const o of collection) {
for (const [k, v] of Object.entries(o)) {
if (k === key && v === value) {
return o
}
if (Array.isArray(v)) {
const _o = _find(v, key, value)
if (_o) {
return _o
}
}
}
}
}
console.log(_find(data, 'channel_id', 'dsa2'))
We use object-scan for our data processing. It's conceptually very simple, but allows for a lot of cool stuff. Here is how you would solve your specific question
// const objectScan = require('object-scan');
const find = (id, input) => objectScan(['**'], {
abort: true,
rtn: 'value',
filterFn: ({ value }) => value.id === id
})(input);
const data = [{ title: 'some title', channel_id: '123we', options: [{ channel_id: 'abc', image: 'http://asdasd.com/all-inclusive-block-img.jpg', title: 'All-Inclusive', options: [{ channel_id: 'dsa2', title: 'Some Recommends', options: [{ image: 'http://www.asdasd.com', title: 'Sandals', id: '1', content: {} }] }] }] }];
console.log(find('1', data));
// => { image: 'http://www.asdasd.com', title: 'Sandals', id: '1', content: {} }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Found the answer I was looking for, especially Ali Alnoaimi's solution. I made some small adjustments to allow for the search of the value as well
function deepSearchByKey(object, originalKey, originalValue, matches = []) {
if (object != null) {
if (Array.isArray(object)) {
for (let arrayItem of object) {
deepSearchByKey(arrayItem, originalKey, originalValue, matches);
}
} else if (typeof object == 'object') {
for (let key of Object.keys(object)) {
if (key == originalKey) {
if (object[key] == originalValue) {
matches.push(object);
}
} else {
deepSearchByKey(object[key], originalKey, originalValue, matches);
}
}
}
}
return matches;
}
To use:
let result = deepSearchByKey(arrayOrObject, 'key', 'value');
This will return the object containing the matching key and value.
#Iulian Pinzaru's answer was almost exactly what I needed, but it doesn't work if your objects have any null values. This version fixes that.
function deepSearch (object, key, predicate) {
if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object
for (let i = 0; i < Object.keys(object).length; i++) {
const nextObject = object[Object.keys(object)[i]];
if (nextObject && typeof nextObject === "object") {
let o = deepSearch(nextObject, key, predicate)
if (o != null) return o
}
}
return null
}
function getPropFromObj(obj, prop) {
let valueToFindByKey;
if (!Array.isArray(obj) && obj !== null && typeof obj === "object") {
if (obj.hasOwnProperty(prop)) {
valueToFindByKey = obj[prop];
console.log(valueToFindByKey);
} else {
let i;
for (i = 0; i < Object.keys(obj).length; i++) {
getPropFromObj(obj[Object.keys(obj)[i]], prop);
}
}
}
return null;
}
const objToInvestigate = {
employeeInformation: {
employees: {
name: "surya",
age: 27,
job: "Frontend Developer",
},
},
};
getPropFromObj(objToInvestigate, "name");
Detecting the key in the deeply nested object.
Finally return the value of the detected key.
Improved answer to take into account circular references within objects.
It also displays the path it took to get there.
In this example, I am searching for an iframe that I know is somewhere within a global object:
const objDone = []
var i = 2
function getObject(theObject, k) {
if (i < 1 || objDone.indexOf(theObject) > -1) return
objDone.push(theObject)
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = getObject(theObject[i], i);
if (result) {
break;
}
}
}
else
{
for(var prop in theObject) {
if(prop == 'iframe' && theObject[prop]) {
i--;
console.log('iframe', theObject[prop])
return theObject[prop]
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
result = getObject(theObject[prop], prop);
if (result) {
break;
}
}
}
}
if (result) console.info(k)
return result;
}
Running the following:
getObject(reader, 'reader')
gave the following output and the iframe element in the end:
iframe // (The Dom Element)
_views
views
manager
rendition
book
reader
NOTE: The path is in reverse order reader.book.rendition.manager.views._views.iframe
I'd like to suggest an amendment to Zach/RegularMike's answer (but don't have the "reputation" to be able to comment!). I found there solution a very useful basis, but suffered in my application because if there are strings within arrays it would recursively call the function for every character in the string (which caused IE11 & Edge browsers to fail with "out of stack space" errors). My simple optimization was to add the same test used in the "object" clause recursive call to the one in the "array" clause:
if (arrayElem instanceof Object || arrayElem instanceof Array) {
Thus my full code (which is now looking for all instances of a particular key, so slightly different to the original requirement) is:
// Get all instances of specified property deep within supplied object
function getPropsInObject(theObject, targetProp) {
var result = [];
if (theObject instanceof Array) {
for (var i = 0; i < theObject.length; i++) {
var arrayElem = theObject[i];
if (arrayElem instanceof Object || arrayElem instanceof Array) {
result = result.concat(getPropsInObject(arrayElem, targetProp));
}
}
} else {
for (var prop in theObject) {
var objProp = theObject[prop];
if (prop == targetProp) {
return theObject[prop];
}
if (objProp instanceof Object || objProp instanceof Array) {
result = result.concat(getPropsInObject(objProp, targetProp));
}
}
}
return result;
}
Some time ago I have made a small lib find-and, which is available on npm, for working with nested objects in a lodash manner. There's the returnFound function which returns the found object, or an object array if there's more than one object found.
E.g.,
const findAnd = require('find-and');
const a = [
{
'title': "some title",
'channel_id':'123we',
'options': [
{
'channel_id':'abc',
'image':'http://asdasd.com/all-inclusive-block-img.jpg',
'title':'All-Inclusive',
'options':[
{
'channel_id':'dsa2',
'title':'Some Recommends',
'options':[
{
'image':'http://www.asdasd.com',
'title':'Sandals',
'id':'1',
'content':{},
},
],
},
],
},
],
},
];
findAnd.returnFound(a, {id: '1'});
returns
{
'image':'http://www.asdasd.com',
'title':'Sandals',
'id':'1',
'content':{},
}
function getPath(obj, path, index = 0) {
const nestedKeys = path.split('.')
const selectedKey = nestedKeys[index]
if (index === nestedKeys.length - 1) {
return obj[selectedKey]
}
if (!obj.hasOwnProperty(selectedKey)) {
return {}
}
const nextObj = obj[selectedKey]
return Utils.hasPath(nextObj, path, index + 1)
}
You're welcome
By: Gorillaz
This function (main()) allows you to get all objects within a JSON whose key is user-defined. Here is an example:
function main(obj = {}, property) {
const views = [];
function traverse(o) {
for (var i in o) {
if (i === property) views.push(o[i]);
if (!!o[i] && typeof(o[i]) == "object") traverse(o[i]);
}
}
traverse(obj);
return views;
}
const obj = {
id: 'id at level 1',
level2: {
id: 'id at level 2',
level3: {
id: 'id at level 3',
level4: {
level5: {
id: 'id at level 5'
}
}
}
},
text: ''
}
console.log(main(obj, 'id'));
If you're already using Underscore, use _.find()
_.find(yourList, function (item) {
return item.id === 1;
});

Set value in array to key in object reduce/return value inside object

The following should put out "Yuval"
console.log(pathFind(["book", "author", "name"], {
book: {
author: {
name: "Yuval"
}
}
}));
I've tried writing this function but it keeps returning undefined:
function pathFind(path, object) {
return path.reduce((accumulator, name) => {
if(accumulator && accumulator[name] != typeof 'object') {
accumulator[name]
} else {
undefined, object
}
})
}
What am I missing? (a typo?)
Is there a way to use recursion inside this function in combination with reduce? (i.e how to approach recursion for this?)
Try the following:
var obj = {
book: {
author: {
name: "Yuval"
}
}
};
var path = ["book", "author", "name"];
function findPath(obj, path){
if(path.length === 0) return obj;
return findPath(obj[path[0]], path.slice(1));
}
console.log(findPath(obj,path));

Javascript beyond property accessors

I read a lot about JS accessors here and figure out this gonna be good for me:
This is what I used for local fields:
TYPE_DEFAULT_VALUE= {
number: 0,
string: "",
array: [],
object: {},
};
typeOf = function (object) {
if (typeof object === "number" && isNaN(object))
return NaN;
try {
return Object.prototype.toString.call(object).slice(8, -1).toLowerCase();
}
catch(ex) {
return "N/A";
};
};
getAccessor = function(obj, key, type, defaultValue) {
if (defaultValue === undefined)
defaultValue = TYPE_DEFAULT_VALUE[type] === undefined ? null : TYPE_DEFAULT_VALUE[type];
return {
enumerable: true,
configurable: true,
get: function () {
if (obj[key] === undefined)
obj[key] = defaultValue;
return obj[key];
},
set: function (value) {
if (typeOf(value) === type)
obj[key] = value;
},
};
}
LocalFields = function (fields, object) {
/**
* field properties
* {
* type: [ required ] ( number | string | array | object | ... ),
* defaultValue: [ optional ]
* }
*/
if (! fields)
throw "Too few parameters ...";
if (! object)
object = this;
var obj = this;
var fieldsAccessor = {};
for(key in fields){
field = fields[key];
fieldHandler = key[0].toUpperCase() + key.substr(1);
if(! field.type)
throw "Type not set for field: " + key;
fieldsAccessor[fieldHandler] = getAccessor(obj, fieldHandler, field.type, field.defaultValue)
}
Object.defineProperties(object, fieldsAccessor);
}
Now for each Class I can just call something like:
Person = function(){
new LocalFields({
id: { type: "number" },
name: { type: "string" },
phone: { type: "array" },
}, this);
}
And then like VS getter and setter you'll call:
var alex = new Person();
alex.Name = "Alex Ramsi";
console.clear();
console.info(alex.Name);
this works for all types but there is a problem because getter and setter is the basic operation and what if I want to add an array field and call this append method or even prepend is there anyhow anyway to do that?
For example How can I call:
alex.Phone.append('+1234567890');
That's a good effort but you forgot that there is no append function for array list!
You can use push and any other array functionality. Check it again;

Convert returned JSON Object Properties to (lower first) camelCase

I have JSON returned from an API like so:
Contacts: [{ GivenName: "Matt", FamilyName: "Berry" }]
To keep this consistent with my code style (camelCase - lower case first letter) I want to transform the array to produce the following:
contacts: [{ givenName: "Matt", familyName: "Berry" }]
What's the easiest/best way to do this? Create a new Contact object and iterate over all the contacts in the returned array?
var jsonContacts = json["Contacts"],
contacts= [];
_.each(jsonContacts , function(item){
var contact = new Contact( item.GivenName, item.FamilyName );
contacts.push(contact);
});
or can I map the original array or transform it somehow?
If you would use lodash instead of underscore, this would do:
_.mapKeys(obj, (v, k) => _.camelCase(k))
This would convert both TitleCase and snake_case to camelCase. Note that it is not recursive though.
Here's a reliable, recursive function that will properly camelCase all of a JavaScript object's properties:
function toCamel(o) {
var newO, origKey, newKey, value
if (o instanceof Array) {
return o.map(function(value) {
if (typeof value === "object") {
value = toCamel(value)
}
return value
})
} else {
newO = {}
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString()
value = o[origKey]
if (value instanceof Array || (value !== null && value.constructor === Object)) {
value = toCamel(value)
}
newO[newKey] = value
}
}
}
return newO
}
Test:
var obj = {
'FirstName': 'John',
'LastName': 'Smith',
'BirthDate': new Date(),
'ArrayTest': ['one', 'TWO', 3],
'ThisKey': {
'This-Sub-Key': 42
}
}
console.log(JSON.stringify(toCamel(obj)))
Output:
{
"firstName":"John",
"lastName":"Smith",
"birthDate":"2017-02-13T19:02:09.708Z",
"arrayTest": [
"one",
"TWO",
3
],
"thisKey":{
"this-Sub-Key":42
}
}
You can do this with this recursive function (with lodash and ES6):
import { camelCase } from 'lodash';
const camelizeKeys = (obj) => {
if (Array.isArray(obj)) {
return obj.map(v => camelizeKeys(v));
} else if (obj != null && obj.constructor === Object) {
return Object.keys(obj).reduce(
(result, key) => ({
...result,
[camelCase(key)]: camelizeKeys(obj[key]),
}),
{},
);
}
return obj;
};
Test:
const obj = {
'FirstName': 'John',
'LastName': 'Smith',
'BirthDate': new Date(),
'ArrayTest': ['one', 'TWO', 3],
'ThisKey': {
'This-Sub-Key': 42
}
}
console.log(JSON.stringify(camelizeKeys(obj)))
Output:
{
"firstName": "John",
"lastName": "Smith",
"birthDate": "2018-05-31T09:03:57.844Z",
"arrayTest":[
"one",
"TWO",
3
],
"thisKey":{
"thisSubKey": 42
}
}
To change a plain object's keys from snake_case to camelCase recursively try the following
(which uses Lodash):
function objectKeysToCamelCase(snake_case_object) {
var camelCaseObject = {};
_.forEach(
snake_case_object,
function(value, key) {
if (_.isPlainObject(value) || _.isArray(value)) { // checks that a value is a plain object or an array - for recursive key conversion
value = objectKeysToCamelCase(value); // recursively update keys of any values that are also objects
}
camelCaseObject[_.camelCase(key)] = value;
}
)
return camelCaseObject;
};
test in this PLUNKER
Note: also works recursively for objects within arrays
Using lodash and ES6, this will replace all keys recursively to camelcase:
const camelCaseKeys = (obj) => {
if (!_.isObject(obj)) {
return obj;
} else if (_.isArray(obj)) {
return obj.map((v) => camelCaseKeys(v));
}
return _.reduce(obj, (r, v, k) => {
return {
...r,
[_.camelCase(k)]: camelCaseKeys(v)
};
}, {});
};
Just use humps
humps.camelize('hello_world');
humps.camelizeKeys(object, options); // will work through entire object
https://www.npmjs.com/package/humps
This is a great use case for axios interceptors
Basically, define a client class and attach a before/after interceptor that converts the request/response data.
export default class Client {
get(url, data, successCB, catchCB) {
return this._perform('get', url, data, successCB, catchCB);
}
post(url, data, successCB, catchCB) {
return this._perform('post', url, data, successCB, catchCB);
}
_perform(method, url, data, successCB, catchCB) {
// https://github.com/axios/axios#interceptors
// Add a response interceptor
axios.interceptors.response.use((response) => {
response.data = toCamelCase(response.data);
return response;
}, (error) => {
error.data = toCamelCase(error.data);
return Promise.reject(error);
});
// Add a request interceptor
axios.interceptors.request.use((config) => {
config.data = toSnakeCase(config.data);
return config;
}, (error) => {
return Promise.reject(error);
});
return axios({
method: method,
url: API_URL + url,
data: data,
headers: {
'Content-Type': 'application/json',
},
}).then(successCB).catch(catchCB)
}
}
Here's a gist with a longer example using React/axios.
there's a nice npm module for this..
https://www.npmjs.com/package/camelcase-keys
npm install camelcase-keys
const camelcaseKeys = require( "camelcase-keys" );
camelcaseKeys( { Contacts: [ { GivenName: "Matt", FamilyName: "Berry" } ] }, { deep: true } );
will return...
{ contacts: [ { givenName: "Matt", familyName: "Berry" } ] }
This solution based on the plain js solution above, uses loadash and Keeps an array if passed as a parameter and Only change the Keys
function camelCaseObject(o) {
let newO, origKey, value
if (o instanceof Array) {
newO = []
for (origKey in o) {
value = o[origKey]
if (typeof value === 'object') {
value = camelCaseObject(value)
}
newO.push(value)
}
} else {
newO = {}
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newO[_.camelCase(origKey)] = o[origKey]
}
}
}
return newO
}
// Example
const obj = [
{'my_key': 'value'},
{'Another_Key':'anotherValue'},
{'array_key':
[{'me_too':2}]
}
]
console.log(camelCaseObject(obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Using lodash, you can do it like this:
export const toCamelCase = obj => {
return _.reduce(obj, (result, value, key) => {
const finalValue = _.isPlainObject(value) || _.isArray(value) ? toCamelCase(value) : value;
return { ...result, [_.camelCase(key)]: finalValue };
}, {});
};
Well I took up the challenge and think I figured it out:
var firstToLower = function(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
};
var firstToUpper = function(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
};
var mapToJsObject = function(o) {
var r = {};
$.map(o, function(item, index) {
r[firstToLower(index)] = o[index];
});
return r;
};
var mapFromJsObject = function(o) {
var r = {};
$.map(o, function(item, index) {
r[firstToUpper(index)] = o[index];
});
return r;
};
// Map to
var contacts = [
{
GivenName: "Matt",
FamilyName: "Berry"
},
{
GivenName: "Josh",
FamilyName: "Berry"
},
{
GivenName: "Thomas",
FamilyName: "Berry"
}
];
var mappedContacts = [];
$.map(contacts, function(item) {
var m = mapToJsObject(item);
mappedContacts.push(m);
});
alert(mappedContacts[0].givenName);
// Map from
var unmappedContacts = [];
$.map(mappedContacts, function(item) {
var m = mapFromJsObject(item);
unmappedContacts.push(m);
});
alert(unmappedContacts[0].GivenName);
Property converter (jsfiddle)
The trick is handling the objects as arrays of object properties.
Here's handy library you might wanna try:
https://www.npmjs.com/package/camelize2
You simply need to install it with npm install --save camelize2 and then
const camelize = require('camelize2')
const response = {
Contacts: [{ GivenName: "Matt", FamilyName:"Berry" }]
}
const camelizedResponse = camelize(response)
Solution similar to #brandonscript, but in more ES6-functional way:
const camelCaseString = str => (
(str.charAt(0).toLowerCase() + str.slice(1) || str).toString()
);
const objectToCamelCase = val => {
if (typeof val != 'object' || val === null) {
return val;
}
if (val instanceof Array) {
return val.map(objectToCamelCase);
}
return Object.keys(val)
.filter(prop => val.hasOwnProperty(prop))
.map(prop => ({[camelCaseString(prop)]: objectToCamelCase(val[prop])}))
.reduce((prev, current) => ({...prev, ...current}))
};
// Example:
let converted = objectToCamelCase({UserId: 1, Hobbies: [{Id: 1, Label: "Read"}], Name: "John Doe"});
console.log(converted)
I needed a generic method that accepted an array or object. This is what I'm using (I borrowed KyorCode's firstToLower() implementation):
function convertKeysToCamelCase(obj) {
if (!obj || typeof obj !== "object") return null;
if (obj instanceof Array) {
return $.map(obj, function(value) {
return convertKeysToCamelCase(value);
});
}
var newObj = {};
$.each(obj, function(key, value) {
key = key.charAt(0).toLowerCase() + key.slice(1);
if (typeof value == "object" && !(value instanceof Array)) {
value = convertKeysToCamelCase(value);
}
newObj[key] = value;
});
return newObj;
};
Example calls:
var contact = { GivenName: "Matt", FamilyName:"Berry" };
console.log(convertKeysToCamelCase(contact));
// logs: Object { givenName="Matt", familyName="Berry"}
console.log(convertKeysToCamelCase([contact]));
// logs: [Object { givenName="Matt", familyName="Berry"}]
console.log(convertKeysToCamelCase("string"));
// logs: null
console.log(contact);
// logs: Object { GivenName="Matt", FamilyName="Berry"}
Took the challenge with lodash and some es6+ features
Here is my implementation with the reduce function.
function deeplyToCamelCase(obj) {
return _.reduce(obj, (camelCaseObj, value, key) => {
const convertedDeepValue = _.isPlainObject(value) || _.isArray(value)
? deeplyToCamelCase(value)
: value;
return { ...camelCaseObj, [_.camelCase(key)] : convertedDeepValue };
}, {});
};
Use lodash ...
function isPrimitive (variable) {
return Object(variable) !== variable
}
function toCamel (variable) {
if (isPrimitive(variable)) {
return variable
}
if (_.isArray(variable)) {
return variable.map(el => toCamel(el))
}
const newObj = {}
_.forOwn(variable, (value, key) => newObj[_.camelCase(key)] = toCamel(value))
return newObj
}
This function loop recursively through the object keys and using lodash returns a new object with every field converted to camelCase. It works also with arrays, nested arrays, nested objects.
function deepCamelCase (obj) {
const c = {}
if (typeof obj !== 'object') return obj
_.mapKeys(obj, (v, k) => {
let w = {}
if (typeof v === 'object') {
if (Array.isArray(v)) {
const k = []
for (const i of v) {
k.push(deepCamelCase(i))
}
} else {
_.mapValues(v, (n, m) => {
if (Array.isArray(n)) {
const k = []
for (const i of n) {
k.push(deepCamelCase(i))
}
w[_.camelCase(m)] = k
} else {
w[_.camelCase(m)] = deepCamelCase(n)
}
})
}
} else {
w = v
}
c[_.camelCase(k)] = w
})
return c
}
Updated code using the reference from https://plnkr.co/edit/jtsRo9yU12geH7fkQ0WL?p=preview
This handles the Objects with array with objects inside it too and so on, by keeping arrays as arrays (which you can iterate over using map)
function snakeToCamelCase(snake_case_object){
var camelCaseObject;
if (isPlainObject(snake_case_object)) {
camelCaseObject = {};
}else if(isArray(snake_case_object)){
camelCaseObject = [];
}
forEach(
snake_case_object,
function(value, key) {
if (isPlainObject(value) || isArray(value)) {
value = snakeToCamelCase(value);
}
if (isPlainObject(camelCaseObject)) {
camelCaseObject[camelCase(key)] = value;
}else if(isArray(camelCaseObject)){
camelCaseObject.push(value);
}
}
)
return camelCaseObject;
}
This is my take; more readable and with less nesting than brandoncode's implementation, and with more room for handling edge cases like Date (which isn't handled, by the way) or null:
function convertPropertiesToCamelCase(instance) {
if (instance instanceof Array) {
var result = [];
for (var i = 0; i < instance.length; i++) {
result[i] = convertPropertiesToCamelCase(instance[i]);
}
return result;
}
if (typeof instance != 'object') {
return instance;
}
var result = {};
for (var key in instance) {
if (!instance.hasOwnProperty(key)) {
continue;
}
result[key.charAt(0).toLowerCase() + key.substring(1)] = convertPropertiesToCamelCase(instance[key]);
}
return result;
}
Building on goredwards answer (which didn't handle the array fields correctly)
function objectKeysToCamelCase(snake_case_object) {
let camelCaseObject = {}
_.forEach(
snake_case_object,
function(value, key) {
if (_.isPlainObject(value)) {
value = objectKeysToCamelCase(value)
} else if (_.isArray(value)) {
value = value.map(v => _.isPlainObject(v) ? objectKeysToCamelCase(v) : v)
}
camelCaseObject[_.camelCase(key)] = value
},
)
return camelCaseObject
}
here is code I found for it, not fully tested though, but worth sharing.
It is far more readable than other answers, not sure about performance.
test it http://jsfiddle.net/ms734bqn/1/
const toCamel = (s) => {
return s.replace(/([-_][a-z])/ig, ($1) => {
return $1.toUpperCase()
.replace('-', '')
.replace('_', '');
});
};
const isArray = function (a) {
return Array.isArray(a);
};
const isObject = function (o) {
return o === Object(o) && !isArray(o) && typeof o !== 'function';
};
const keysToCamel = function (o) {
if (isObject(o)) {
const n = {};
Object.keys(o)
.forEach((k) => {
n[toCamel(k)] = keysToCamel(o[k]);
});
return n;
} else if (isArray(o)) {
return o.map((i) => {
return keysToCamel(i);
});
}
return o;
};
Pure JavaScript, shoud work fine
function convertKeysToCamelCase(object) {
if(object === undefined || object === null || typeof object !== "object") {
return object;
} else {
if(Array.isArray(object)) {
return object.map(item => convertKeysToCamelCase(item));
} else {
return Object.entries(object).reduce((result, [key, value]) => {
result[key.charAt(0).toLowerCase() + key.slice(1)] = convertKeysToCamelCase(value);
return result;
}, {});
}
}
}
you can do this simply by using json-case-convertor
const jcc = require('json-case-convertor')
const jsonData = ''//you json data to convert
const camelCasedJson = jcc.camelCaseKeys(jsonData) //Convert all the keys of object to snake case
This will handle all cascaded object as well
Convert object keys to camelCase with deep.
import _ from 'lodash';
export function objectKeysToCamelCase(entity) {
if (!_.isObject(entity)) return entity;
let result;
result = _.mapKeys(entity, (value, key) => _.camelCase(key));
result = _.mapValues(result, (value) => objectKeysToCamelCase(value));
return result;
}

Categories