How to convert a nested object to a string indexed object - javascript

I am trying to convert a nested object to a string indexed object, so I can use Vue to display all properties in an object. For example:
var obj = {
key1: 'value1',
key2: {
key3: {
key5: 'value5',
key6: 'value6'
},
key4: 'value4'
}
};
Should be convert to this:
var obj = {
'key1': 'value1',
'key2.key3.key5': 'value5',
'key2.key3.key6': 'value6',
'key2.key4': 'value4'
}
I tried to walk recursively through the object, but I didn't figure out how to get the correct index value and return both the index and the object at the same time.
What I've tried so far:
// let result = objConversion(obj)
objConversion (obj) {
let resultObject = {}
// Walk through the first level
for (let index in obj) {
if (obj.hasOwnProperty(index)) {
let extractedObj = getObjNameRecursive(obj[ index ], index)
resultObject = { ...resultObject, ...extractedObj }
}
}
return resultObject
}
getObjNameRecursive (obj, name) {
let resultObject = {}
if (typeof obj === 'object') {
// Dive into an object
for (let index in obj) {
if (obj.hasOwnProperty(index)) {
if (typeof obj[ 'index' ] === 'object') {
resultObject = { ...resultObject, ...getObjNameRecursive(obj[ 'index' ], name + '.' + index) }
} else {
resultObject = {...resultObject, [name + '.' + index]: obj[index]}
}
}
}
} else {
// Simple assign if not an object
resultObject[ name ] = obj
}
return resultObject
}
But this gives the result like:
obj = {
'key1': 'value1',
'key2.key3.key5': [object Object],
'key2.key3.key6': [object Object],
'key2.key4': 'value4'
}
The answer in Convert string to an attribute for a nested object in javascript is very close to what I want. But what I want is to get the string of attributes of a nested object.
Is there any better way to do this?
Thanks.

Try this
function convert(obj, key, result) {
if(typeof obj !== 'object') {
result[key] = obj;
return result;
}
const keys = Object.keys(obj);
for(let i = 0; i < keys.length; i++){
const newKey = key ? (key + '.' + keys[i]) : keys[i];
convert(obj[keys[i]], newKey, result);
}
return result;
}
call it
convert(obj, '', {});

Related

Get the key of a JS Object from the value

I am trying to get the key of a JS Object in Typescript from an input value, the problem is that the values are inside an Array.
This is what I have seen in the case that the value is not in an array:
const exampleObject = {
key1: 'Geeks',
key2: 'Javascript'
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // key1
This is an example of my object that I want to get the key from:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // undefined
I need to get the key without using Array.prototype.includes(), because typing the resulting variable as String gives me an error that it may be undefined.
My problem: I don't know how to go through the array inside the function to find the value and get the key
Update:
What I want is to avoid the possibility of returning an undefined, since the input value will always be inside the object, how can I achieve this?
#segmet
You can use my code
const exampleObject = {
key1: ['Geeks', 'test1', 'test2'],
key2: ['Javascript', 'test3', 'test4']
};
function getKeyByValue(object, value) {
var output = "";
for (var prop in object) {
// finding and removing element from array
object[prop].find(i => {
if (i === value) {
output = prop;
return prop;
}
}
)
}
return output
}
console.log(getKeyByValue(exampleObject, 'Geeks'))
You can achieve this with a single line of code by using 3 JavaScript methods - Object.keys(), Array.find() & Array.indexOf() :
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
const res = Object.keys(exampleObject).find(key => exampleObject[key].indexOf(value) !== -1);
return res || ''
}
console.log(getKeyByValue(exampleObject,'Geeks'))
function getKeyByValue(object, value) {
const key = Object.entries(object).filter(([key, val]) => val.includes(value));
return Object.fromEntries(key);
}
Try this function instead
You will get the object matching your keys
You can do something like this and then check for null
const getKeyFromValue = (obj:Object, value:string | string[]) => {
for (let key in obj) {
if (typeof value === 'string') {
if (obj[ key ].includes(value)) {
return key;
}
}
else if (Array.isArray(value)) {
if (obj[ key ].every(val => value.includes(val))) {
return key;
}
}
}
return null;
}
One more try:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(obj, data) {
for (const [key, value] of Object.entries(obj)) {
return (~value.indexOf(data)) ? key : false
}
}
console.log(getKeyByValue(exampleObject,'Geeks'))

dynamically flatten nested array of objects javascript

I'm trying to write a function that will accept a nested object array, and dynamically return the flattened result. arrayProperties.filter() is not returning an array of objects like I expect.
const data = [
{
parKeyA: "parValA",
parKeyA1:
{chiKeyA1: "chiValA1", chiKeyA2: "chiValA2"},
parKeyA2: {chiKeyA3: "chiValA3"}
},
{
parKeyB: "parValB",
parKeyB1:
{chiKeyB1:"chiValB1"}
}
]
flatData = flatNestedObjArray(data);
console.log(flatData);
function flatNestedObjArray(array) {
let flatArray = array.map(element => {
let arrayProperties = Object.entries(element);
//filter not returning array of objects
let nestedObjects = arrayProperties.filter(property => {
const parentValue = property[1];
if (typeof parentValue === "object" && parentValue !== null) {
return parentValue;
}
});
//nestedObjects should be array of objects
let merged = nestedObjects.map(obj => element.concat(obj));
return merged;
});
return flatArray;
}
Expected Result:
const data = [
{
parKeyA: "parValA",
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2",
chiKeyA2: "chiValA2"
},
{
parKeyB: "parValB",
chiKeyB1:"chiValB1"
}
]
You can use recursion to flatten the objects into a single level object and pass that function to map to get an array of flattened object
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA3"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1",
chiKeyB2: {}
}
}
]
let flatten = (obj, final = {}) => {
for (let key in obj) {
if (typeof obj[key] === 'object' && obj[key] != null) {
flatten(obj[key], final)
} else {
final[key] = obj[key]
}
}
return final
}
console.log(data.map((v) => flatten(v)))
You can use object property loop using in keyword for each level using recursion
for(var prop in data) {
....
}
I used an old recursion technique to start with a working code
function flatten(data) {
var newData = {};
for(var prop in data) {
if(typeof data[prop] == "object") {
var childs = flatten(data[prop])
for(var cprop in childs){
newData[cprop] = childs[cprop];
}
}else {
newData[prop] = data[prop]
}
}
return newData;
}
for(var i=0;i<data.length;i++)
data[i] = flatten(data[i]);
console.log(data);
You need to handle duplicates
You can use map which will return a array and a recursive function. Have added comment in the code , hopefully that will be useful
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA2"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1"
}
}
]
/* Recursive function.It will take a object,iterate the object and check if the value of the key is another object. If it is another object then call same recursive function */
function getFlatObj(obj) {
let newObject = {}
function doRecurssion(currObj) {
// iterate through the object
for (let keys in currObj) {
// check if the value is another object
if (typeof currObj[keys] === 'object' && typeof currObj[keys] !== null) {
doRecurssion(currObj)
} else {
// if not another object then add key and value
newObject[keys] = currObj[keys]
}
}
return newObject;
}
return doRecurssion(obj);
}
let flatObj = data.map((item) => {
const acc = {};
for (let keys in item) {
if (typeof item[keys] === 'object' && typeof item[keys] !== null) {
Object.assign(acc, getFlatObj(item[keys]))
} else {
acc[keys] = item[keys]
}
}
return acc;
}, {});
console.log(flatObj)
Object.entries() takes an object and converts it into a two-dimensional array:
let object = {keyA: "valueA", keyB: "valueB", keyC: {kA: "vA", kB: "vB"}};
let array = Object.entries(object);
// array = [["keyA", "valueA"], ["keyB", "valueB"], ["keyC", {kA: "vA", kB: "vB"}]];
Using the above within a for...of loop, each entry can be destructured:
for (let [key, value] of Object.entries(object)) {...
Declare an empty array and iterate through each object literal within the array of objects:
let array = [];
for (let obj of objArray) {...
On each object, declare an empty object and then convert each key/value of each object into a sub-array:
let object = {};
for (let [key, value] of Object.entries(obj)) {...
Check each value of each object literal -- if the value is an object literal...
if (Object.prototype.toString.call(value) == "[object Object]") {...
...iterate through the value and assign each key/value to the empty object...
for (let [k, v] of Object.entries(value)) {
object[k] = v;
}
...otherwise assign the key/value to the empty object...
} else {
object[key] = value;
}
Push the new object to the new array:
array.push(object);
Demo
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA3"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1"
}
}
];
function subObjToKeyVal(objArr) {
let array = [];
for (let obj of objArr) {
let object = {};
for (let [key, value] of Object.entries(obj)) {
if (Object.prototype.toString.call(value) == "[object Object]") {
for (let [k, v] of Object.entries(value)) {
object[k] = v;
}
} else {
object[key] = value;
}
}
array.push(object);
}
return array;
}
console.log(subObjToKeyVal(data));

Find the key in an object which contains a particular value in an array

I need to find the key(type:array) which contains a specific string. The actual data would look like as provided below
I tried to inverse the particular object with _.invert(). But doesnt make sense as it would convert the entire array to a string
{
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
The response desired is that with we can fetch the key_name if an array element is provided.
ie if the input is lets 'asd' we need to be able to say that the key was key1
Take the keys and find the element with includes.
const
find = (object, value) => Object.keys(object).find(k => object[k].includes(value)),
object = { key1: ['asd','yrt','uyt'], key2: ['nbm','inb','oiu'] },
key = find(object, 'asd');
console.log(key);
How often are you going to do this? If only once, then:
data = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
needle = 'oiu'
magicKey = Object.keys(data).filter(key => data[key].includes(needle))
Otherwise you're going to want to make a new dictionary that has your possible needles as keys!
const obj = { key1: ['asd','yrt','uyt'], key2: ['nbm','inb','oiu']}
const keyResult = Object.entries(obj).find(element => element[1].includes('asd'))
console.log(keyResult[0] || 'not found')
It might look like this:
let elementToFind = "asd";
let object = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
let output;
for(let key in object) { // iterate over every key in object
if(object.hasOwnProperty(key)) { // check if we don't try to access one of object's prototype fields etc.
if(object[key].includes(elementToFind)) {
// check if object[key] which is array, contains element we look for
// if so, break the loop to prevent it from further searching
output = key;
break;
}
}
}
let search = 'asd'
let obj = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
Object.keys(obj).find(key => obj[key].indexOf(search) > -1)
If there can be multiple such keys use filter instead of find.
One method would be to iterate over the key/value produced from Object.entries, and return the key where the string is found in the value (array).
const obj = {
key1: ['asd','yrt','uyt'],
key2: ['nbm','inb','oiu']
}
function getKey(obj, str) {
for (let [key, arr] of Object.entries(obj)) {
if (arr.includes(str)) return key;
}
return null;
}
console.log(getKey(obj, 'inb'));
console.log(getKey(obj, 'asd'));
You could write a function that recursively returns the path...
const hasKey = (key, data) => ({}).hasOwnProperty.call(data, key);
const typeOf = value => ({}).toString.call(value).replace(/\[object (\w+)]/, '$1');
const isLeaf = value => ['String', 'Boolean', 'Null', 'Undefined', 'Number', 'Date', 'Function'].includes(typeOf(value));
const needle = 'asd';
const haystack = {
"key1": ['asd1','yrt','uyt'],
"key2": ['nbm','inb','oiu'],
key3: {
key4: ['asd'],
}
};
const find = (needle, haystack, path = []) => {
if (isLeaf(haystack)) {
return needle === haystack && path;
}
for (let [key, value] of Object.entries(haystack)) {
const result = find(needle, value, path.concat(key));
if (result) { return result; }
}
};
console.log('find', find(needle, haystack));

Lowercase JavaScript object values

I have an array of JavaScript objects:
var obj = [{key1: Value1, key2: VALUE2},{key1: Value1, key2: VALUE2}];
I want the value of the key2 to be in lowercase, like this:
var obj = [{key1: Value1, key2: value2},{key1: Value1, key2: value2}];
How can I change the value from lowercase to uppercase?
If I understand your question correctly, you want to change the value of each object's key2 property, to be a lowercase string (I'm assuming they're strings).
You can do this with a simple map.
obj = obj.map(function(a) {
a.key2 = a.key2.toLowerCase();
return a;
});
There are some differences in all the answers given here, surrounding object references. In my answer above, the array itself will be a new reference, but the objects inside will remain as the same reference. To clarify this by way of example
var mapped = obj.map(function(a) {
a.key2 = a.key2.toLowerCase();
return a;
});
console.log(obj === mapped) // false
console.log(obj[0] === mapped[0]) // true
If you're using a for loop or a forEach array function, then the array reference will remain the same.
See this fiddle for reference.
You can use built-in array functions like forEach or map
// in-place
obj.forEach(function (item) {
item.key2 = item.key2.toLowerCase()
})
or
var mapped = obj.map(function (item) {
return Object.assign(item, { key2: item.key2.toLowerCase() });
})
You just have to loop through the array and lowercase the value of key2 for every object.
var objects = [{key1: "Value1", key2: "VALUE2"},{key1: "Value1", key2: "VALUE2"}];
for(var i = 0;i < objects.length;++i) {
objects[i].key2 = objects[i].key2.toLowerCase();
}
You could iterate the array and the keys, you want to change, and assign the lowercase value if you have a string.
var array = [{ key1: 'Value1', key2: 'value2' }, { key1: 'Value1', key2: 'value2' }];
array.forEach(function (a) {
['key2'].forEach(function (k) {
if (typeof a[k] === 'string') {
a[k] = a[k].toLowerCase();
}
});
});
console.log(array);
A version with a fixed key.
var array = [{ key1: 'Value1', key2: 'value2' }, { key1: 'Value1', key2: 'value2' }];
array.forEach(function (a) {
if (typeof a.key2 === 'string') {
a.key2 = a.key2.toLowerCase();
}
});
console.log(array);
Concept:
Create a function to lowercase an item based on the given key.
Then, iterate items and parse each item on those function.
Create a function to lowercase the item based on the given key:
function lowerObj(obj, key) { obj['' + key] = obj['' + key].toLowerCase(); return obj; }
Iterate items and parse each item on those function
var newObj = obj.map((item) => {return lowerObj(obj, 'your_key')});
So, you will get newObj with lower case for the item with the given key.
Example
function lowerObj(obj, key) {
obj['' + key] = obj['' + key].toLowerCase();
return obj;
}
var obj = [{key1: 'Value01', key2: 'Value02'}, {key1: 'Value11', key2: 'Value12'}];
var newObj = obj.map((item) => {return lowerObj(item, 'key2')});
console.log(JSON.stringify(newObj, null, 3));
caseInsensitivity = function(obj) {
Object.keys(obj).forEach(k => {
if(typeof obj[k] == 'string') {
obj[k] = obj[k].toLowerCase();
}
else if(typeof obj[k] == 'object') {
caseInsensitivity(obj[k]);
}
else {
}
});
return obj;
}
To convert all values even if the values are nested objects or arrays.
Assuming you had a JSON object like.
"params": {
"name": "LOWERNAME"
"phone": "1234567899"
}
Call your object like:
let user = {
name: params.name.toLowerCase(),
phone: params.phone
}
You could modify it to suit your situation!

Best way to reset all values in an Javascript object

My javascript object looks something like:
$scope.display = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
}
Upon some events in my code, I would like to reset these values to undefined like below:
$scope.display = {
current: {
key1: undefined,
key2: [],
key3: undefined
}
}
I use libraries like lodash, however i don't see any function that would perform this.
I know how to do this manually, but I was wondering if there is a "Best practices" way of performing this task.
I would create a helper function returning object structure:
function getDisplayObject() {
return {
current: {
key1: undefined, // or you can omit undefined keys
key2: [],
key3: undefined
}
};
}
$scope.display = getDisplayObject();
So later when you need to reset data you would execute $scope.display = getDisplayObject(); again.
Here is my solution with lodash mapValues function:
var $clearObject = function(value) {
if (_.isString(value)) {
return undefined
};
if (_.isArray(value)) {
return [];
};
};
var $scopeDisplay = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
};
$scopeDisplay.current = _.mapValues($scopeDisplay.current, $clearObject);
console.log($scopeDisplay);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
You would loop the properties of your object like this
for (var key in current){
if (current.hasOwnProperty(key)){
if (typeof current[key] === 'string'){
current[key] = undefined;
} else if (current[key] instanceof Array) {
current[key] = [];
} // else ??? Not sure how you want to handle other types
}
}
Array check subject to some potential problems described in the comments here
What about this?
// If you just want to reset a simple object
let reset = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = undefined
})
}
// If you want to reset a complex object
let recursiveReset = (obj) => {
Object.keys(obj).map(key => {
// Test if it's an Object
if (obj[key] === Object(obj[key])) {
recursiveReset(obj[key])
return
}
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = undefined
})
}
// So you can simply use
reset($scope.display.current)
// or
recursiveReset($scope.display)
test link
In my Angular controller, I do the following:
$scope.user = {
firstname: "",
lastname: "",
displayname: "",
email: "",
password: "",
passwordConfirm: ""
};
// default state of the form
$scope.default = angular.copy($scope.user);
/**
* Resets the form to its default state
* #return {void}
*/
$scope.reset = function () {
$scope.user = angular.copy($scope.default);
}
Initially the scope is empty, so I clone it, and whenever it needs reset, simply call a function. However, without knowing the scope of your project, it's hard to determine the best way to handle it.
I did it in AngularJS controller as:
function item_object() {
var list_item = {
ID: '',
Title: '',
LastName: '',
Contact: '',
City: ''
};
return list_item;
}
//Define the object for the item
$scope.Item = item_object();
// Reset product details
$scope.clear = function () {
$scope.Item = item_object();
}
In this way you do not keep a copy of empty or default object. So no extra cost.
There is no built in way. You would need to loop through and set the defaults based on type.
var x = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
};
_(x.current).forEach(
function(value, key, obj) {
var result = undefined;
if (_.isArray(value)) {
result = [];
}
obj[key] = result;
}
);
console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
Basic idea if you have nested objects
var x = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2',
xxxx: {
keyx1: 'valuex1',
keyx2: ['xa', 'xb'],
keyx3: 'valuex2'
}
}
};
function resetObj (obj) {
_(obj).forEach(
function(value, key, objRef) {
var result = undefined;
if (_.isObject(value)) {
resetObj(objRef[key]);
} else {
if (_.isArray(value)) {
result = [];
}
objRef[key] = result;
}
}
);
}
resetObj(x)
console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
If you are using Jquery, like it seems you are doing, you can do that:
Preapre an empty template of your object:
var emptyTemplate = {
current: {
key1: undefined,
key2: [],
key3: undefined
}
}
and then run:
$scope.display = $.extend(true, {}, emptyTemplate);
If you want to automate it, define some data binding:
var defaults = {
String: undefined,
Array: [],
bool: false
}
Then loop in your object, like other suggestions:
function resetObj (obj) {
_(obj).forEach(
function(value, key, objRef) {
if (_.isObject(value)) {
resetObj(objRef[key]);
} else {
var myvarType = typeOf value;
objRef[key] = defaults[myvarType];
}
}
);
}
I copied this nesting function from other answer, just to add my two cents.
A good way to deal with this would be to use a class instead of an object literal, like:
class current {
constructor() {
this.key1 = undefined;
this.key2 = [];
this.key3 = undefined;
}
}
And whenever you want to clear all the values you can just create a new instance of this class, like:
$scope.display.current = new current();
function isObject (value) {
return value && typeof value === 'object' && value.constructor === Object;
}
const resetObj = (obj, resetVal) => {
if(!isObject(obj)) return;
for(let i in obj){
if(!obj.hasOwnProperty(i)) continue;
if(!isObject(obj[i])){
obj[i] = resetVal;
}else{
resetObj(obj[i], resetVal);
}
}
};
+++++++++++++++++++++++++Typescript solution+++++++++++++++++++++++++++
private deviceSettingsToReset: RemoteDeviceSetting;
// depp copy of response into deviceSettingsToReset variable which need to use in reset functionality in UI.
this.deviceSettingsToReset = JSON.parse(JSON.stringify(response));
// assign value from original copy.
this.selectedAlgorithm = JSON.parse(JSON.stringify(this.deviceSettingsToReset));

Categories

Resources