Extract object name as String from an object in JavaScript - javascript

I have an object where its values are other objects
I would like to extract the name of the object by its parent key as a string, e.g., (input, expected output) = ('home', 'someObj), ('anotherOne', 'anotherObj').
So far I tried the following, but it returns [object Object].
I also tried JSON.stringify(data[key].key1) but does not return what I want. Is there a way to achieve this?
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: someObj
},
'anotherOne': {
key1: anotherObj
}
}
console.log(data)
const key = 'home'
const output = `${data[key].key1}`
console.log(output) // expected output: 'someObj'

Javascript objects don't have names. You'd have to take care yourself and probably add a 'name' field where you set an identifier on an object instance.
You hope, that the 'name' of the object could be like the name of the variable that you assign it to. But that's not true. There is zero connection between the object instance and the variable name.

The problem is that you are assigning the variable value and not its name. Because JS compiler will not keep track of the variable name that it used to assign values to
so,
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: someObj
},
'anotherOne': {
key1: anotherObj
}
}
JS compiles it as
const data = {
'home': {
key1: {
something: 'la' // Doesn't keep track of the original variable name used to assign value.
}
},
'anotherOne': {
key1: {
something: 'be'
}
}
}
And when you fetch data[key].key1. it returns {something: 'la'} which is an object and when you use string template literal `${data[key].key1}`. It typecast it into string as
`${{}}` // Outputs "[object Object]"
So, you need to store the variable names as string
const someObj = {
something: 'la'
}
const anotherObj = {
something: 'be'
}
const data = {
'home': {
key1: 'someObj'
},
'anotherOne': {
key1: 'anotherObj'
}
}
console.log(data)
const key = 'home'
const output = `${data[key].key1}`
console.log(output) // output: 'someObj'
This code will work
You need to set the variable name as string.

Related

Pass value and use as object properties dynamically in Javascript

Is there a way I can pass a parameter value into a function and use it as properties of an object?
const state = {
name: 'xyz',
properties: 'abc'
}
...
const handleStuff = (properties:string) => {
const a = [...state.properties]
if (action.status == true)
{
//some code
return {
...state,
properties: a
}
} else {
//some code
return {
...state,
properties: a
}
}
}
It is still not clear what result you are trying to reach, but generally you can access your properties by [] operator.
if you want just to store state.name value into variable you should do following
const a = state[properties] // a will be 'xyz'
code below will be evaluated as spread operation performed on the string 'xyz'
const a = [...state[properties]] // a will be equal to ['x', 'y', 'z']
in your return statement, where you want to combine object and if you want to assign value to property with name properties (which is 'name' for example) you can
return {
...state,
[properties]: a // name value from the state will be overridden with value of variable a
};

Convert JSON object to object which not in JSON format

I have a JSON object with the structure as below
const inputObj = {
"prop1": "val1",
"prop2": {
"prop2_1": "val2_1",
"prop2_2": "val2_2"
}
"prop3": "val3"
}
My objective: I would like to take the property, including the nested property, and store the result in a txt file, but not in JSON format. To make it clear, here is my expected output in the txt file:
{
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
}
prop3: {
id: 'prop3'
}
}
So far, I could write the non nested property, but still not in the structure which I expected. Here is the result so far:
{
"prop1": "prop1",
"prop3": "prop3"
}
Its still in JSON format, not in the structure that I expected, and the nested property still not caught (I still thinking how to get it)
here is the code so far to make my current result:
const fs = require('fs')
const fileName = "./results.txt"
function getAllKeys(obj, path = [], result = []) {
Object.entries(obj).forEach(([k, v]) => {
if (typeof v === 'object') getAllKeys(v, path.concat(k), result)
else result.push(path.concat(k).join("."))
})
return result
}
const inputToFile = getAllKeys(inputObj)
// console.log(inputToFile)
// result of the console.log
// prop1
// prop2.prop2_1
// prop2.prop2_2
// prop3
const newObj = {}
for (var i = 0; i < inputToFile.length; i++) {
var input = inputToFile[i]
var dotIndex = input.indexOf('.') // to check if its from the nested JSON property of the inputObj
if (dotIndex === -1) {
// no dot or nested property in the JSON
newObj[input] = input.toString()
} else {
// if the input contain dot, which is a nested JSON
}
}
fs.writeFileSync(fileName, JSON.stringfy(newObj))
// if I use above line, the result in the file is as I had mention above. But, if the code is like below:
const finals = JSON.stringfy(newObj)
fs.writeFileSync(fileName, JSON.parse(finals))
// the output in the file is only "[Object object]" without double quote
Update
The reason why I need the result to be formatted like that, is because I want to use react-intl. I already have the locale file (the translation), which looks like the inputObj (the structure). Then, I need to make a file, which like this (below), so the lib could translate it:
import { defineMessages } from 'react-intl';
const MessagesId = defineMessages({
prop1: {
id: 'prop1'
},
prop2_prop2_1: {
id: 'prop2.prop2_1'
},
prop2_prop2_2: {
id: 'prop2.prop2_2'
},
prop3: {
id: 'prop3'
}
})
export default MessagesId;
Thats why, I need it to be not like JSON. Because I already have thousand codes for the translation, but need to define it in the MessagesId. It would be so much takes time rite if I do it manually .__.
Ps: the react-intl is works, the problem is only the converting as my initial questions
This script can handle multiple levels of nestied object.
const outputObj = {};
const convertNestedObj = (obj, parentKey = []) => {
for (key in obj) {
newParentKey = [...parentKey, key];
if (typeof obj[key] === 'object') {
convertNestedObj(obj[key], newParentKey);
} else {
outputObj[newParentKey.join('_')] = { id: newParentKey.join('_') };
}
}
};
convertNestedObj(inputObj);

NodeJS Object Casting with defaults and removing extra keys

Assuming somewhere in NodeJS backend code, we have this object:
{
name : "foo"
secret : "bar"
}
We want to return this object as JSON in response of an HTTP request, but, we don't want it to have the secret, i.e. the return object must be:
{
name : "foo"
}
Assume we are using the latest NodeJS and we can use any 3rd party npm package.
Bonus for default params, for example, what if we want the returned object to always include "age", if it's not present, then set it to 0
{
name : "foo"
age : 0
}
EDIT: To add more context, I am not just trying to remove one particular elements, there could be more unwanted elements:
{
name : "foo"
secret1 : "bar"
secret2 : "bar"
secret3 : "bar"
someSecretThatIdontKnowTheirNameYet : "bar"
}
I just have a prototype, or class, or whatever which says:
{
name: String, required
age: Number, required
}
I am trying to figure what this unknown thing above is. Looking for something like:
cleanedUpObject = clean(rawObject, prototypeOrClassOrSomeOtherThing)
You can use a function, and destructure the object. You can set default values on specific properties. Then you can return a new object with the properties you want.
const formatResponse = ({ name, age = 0 }) => ({
name,
age,
});
const data = {
name : "foo",
secret1 : "bar",
secret2 : "bar",
secret3 : "bar",
someSecretThatIdontKnowTheirNameYet : "bar"
};
console.log(formatResponse(data));
Another option is to reduce the model, and include only the properties that exist in the model. If a property is not found on the object, take the default from the model.
const model = {
name: 'baz',
age: 0
}
const formatResponse = (o) =>
Object.entries(model)
.reduce((r, [k, v]) => {
r[k] = k in o ? o[k] : v;
return r;
}, {});
const data = {
name: "foo",
secret1: "bar",
secret2: "bar",
secret3: "bar",
someSecretThatIdontKnowTheirNameYet: "bar"
};
console.log(formatResponse(data));
Just remove private fields from your data and append default value:
const BLACK_LIST_FIELDS = ["secret"];
const DEFAULT_FIELDS_MAP = {
age: 0,
};
function getResponse(data) {
let safeRes = Object.assign({}, data);
BACK_LIST_FIELDS.forEach((key) => delete safeRes[key]);
Object.keys(DEFAULT_FIELDS_MAP).forEach((key) => {
if (!safeRes[key]) {
safeRes[key] = DEFAULT_FIELDS_MAP[key];
}
});
return safeRes;
}
const data = {
name : "foo",
secret : "bar",
};
console.log(getResponse(data));
Result: { name: 'foo', age: 0 }
best two options I can think of:
toJSON
const o = {
name : "foo",
secret : "bar",
toJSON() {
return {
name: this.name
}
}
}
console.log(JSON.stringify(o));
you can return what you want from toJSON, for more info read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior
Symbol
const secret = Symbol('secret');
const o = {
name : "foo",
[secret]: "bar"
};
console.log(JSON.stringify(o));
console.log(o[secret]);
for more info on Symbol read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
Here’s what I understand from your question:
you have an Object that defines the keys and types of your expected result.
You call this prototypeOrClassOrSomeOtherThing. Let’s call this definition, for short (since boh prototype and class have a defined meaning in JS)
you have another Object that contains a certain function result. Let’s call this input.
you want an Object that consists of only those keys and values from input that are defined in definition.
In case input does not contain a key/value that's defined in definition, use a reasonable default value based on the type defined in definition.
Strategy
we’ll write a function defaultsFromDefinition that creates an object with default values as defined by definition
we then call this function to start out with an object of default values, cleanedUpObject
next we replace this object's values with values from input if and only if input has a matching key/value pair
profit
Code
function defaultsFromDefinition(
definition,
defaultsByType = { String: '', Number: 0 })
{
return Object
.entries(definition)
.reduce((result, [key, type]) => ({
...result,
[key]: defaultsByType[type.name]
}), {})
}
function clean(input, definition) {
const cleanedUpObject = defaultsFromDefinition(definition)
return Object
.keys(cleanedUpObject)
.filter(key => input[key] !== undefined)
.reduce((result, key) => ({
...result,
[key]: input[key]
}), cleanedUpObject)
}
// ---- Test ----
const rawObject = {
name : "foo",
secret1 : "bar",
secret2 : "bar",
secret3 : "bar",
someSecretThatIdontKnowTheirNameYet : "bar"
}
const definition = {
name: String,
age: Number
}
const cleanedUpObject = clean(rawObject, definition)
console.log(cleanedUpObject)

How can I get console.log to output the getter result instead of the string "[Getter/Setter]"?

In this code:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I would like to get { _id: 123, id: 123 }
but instead I get { _id: 123, id: [Getter/Setter] }
Is there a way to have the getter value be used by the console.log function?
You can use console.log(Object.assign({}, obj));
Use console.log(JSON.stringify(obj));
Since Nodejs v11.5.0 you can set getters: true in the util.inspect options. See here for docs.
getters <boolean> | <string> If set to true, getters are inspected. If set to 'get', only getters without a corresponding setter are inspected. If set to 'set', only getters with a corresponding setter are inspected. This might cause side effects depending on the getter function. Default: false.
You can define an inspect method on your object, and export the properties you are interested in. See docs here: https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
I guess it would look something like:
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
};
Cls.prototype.inspect = function(depth, options) {
return `{ 'id': ${this._id} }`
}
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
I needed a pretty printed object without the getters and setters yet plain JSON produced garbage. For me as the JSON string was just too long after feeding JSON.stringify() a particularly big and nested object. I wanted it to look like and behave like a plain stringified object in the console. So I just parsed it again:
JSON.parse(JSON.stringify(largeObject))
There. If you have a simpler method, let me know.
On Node.js, I suggest using util.inspect.custom, which will allow you to pretty print getters as values, while keeping other properties output unchanged.
It will apply to your specific object only and won't mess the general console.log output.
The main benefit vs Object.assign is that it happens on your object, so you keep the regular generic console.log(object) syntax. You don't have to wrap it with console.log(Object.assign({}, object)).
Add the following method to your object:
[util.inspect.custom](depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
Here is a working example in your context:
const util = require('util');
function Cls() {
this._id = 0;
Object.defineProperty(this, 'id', {
get: function() {
return this._id;
},
set: function(id) {
this._id = id;
},
enumerable: true
});
this[util.inspect.custom] = function(depth, options) {
const getters = Object.keys(this);
/*
for getters set on prototype, use instead:
const prototype = Object.getPrototypeOf(this);
const getters = Object.keys(prototype);
*/
const properties = getters.map((getter) => [getter, this[getter]]);
const defined = properties.filter(([, value]) => value !== undefined);
const plain = Object.fromEntries(defined);
const object = Object.create(this, Object.getOwnPropertyDescriptors(plain));
// disable custom after the object has been processed once to avoid infinite looping
Object.defineProperty(object, util.inspect.custom, {});
return util.inspect(object, {
...options,
depth: options.depth === null ? null : options.depth - 1,
});
}
};
var obj = new Cls();
obj.id = 123;
console.log(obj);
console.log(obj.id);
Output:
Cls { _id: 123, id: 123 }
123
Use spread operator:
console.log({ ... obj });

Is there any way to use a numeric type as an object key?

It seems that when I use a numeric type as a key name in an object, it always gets converted to a string. Is there anyway to actually get it to store as a numeric? The normal typecasting does not seem to work.
Example:
var userId = 1;
console.log( typeof userId ); // number
myObject[userId] = 'a value';
console.dir(myObject);
Dir Output:
{
'1': 'a value'
}
What I want is this:
{
1: 'a value'
}
Advice?
No, this is not possible. The key will always be converted to a string. See Property Accessor docs
Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method.
> var foo = {}
undefined
> foo[23213] = 'swag'
'swag'
> foo
{ '23213': 'swag' }
> typeof(Object.keys(foo)[0])
'string'
In an object, no, but I have found Map extremely useful for this application. Here is where I have used it for numeric keys, a key-based event.
onKeydown(e) {
const { toggleSidebar, next, previous } = this.props;
const keyMapping = new Map([
[ 83, toggleSidebar ], // user presses the s button
[ 37, next ], // user presses the right arrow
[ 39, previous ] // user presses the left arrow
]);
if (keyMapping.has(e.which)) {
e.preventDefault();
keyMapping.get(e.which)();
}
}
Appears to be by design in ECMA-262-5:
The Property Identifier type is used to associate a property name with a Property Descriptor. Values of the Property Identifier type are pairs of the form (name, descriptor), where name is a String and descriptor is a Property Descriptor value.
However, I don't see a definite specification for it in ECMA-262-3.
Regardless, I wouldn't attempt to use non-strings as property names.
you can use, Map if you want different datatype as keys
const map1 = new Map();
map1.set(1,3)
map1.set('1','string')
// expected output: 3
console.log(map1.get(1)) //output 3;
console.log(map1.get('1')) //output 'string';
Here is the solution. Please tell me the environmental setups if this is not working
const screens = {
"768": "large",
"200": "small"
}
const keys = Object.keys(screens).map(key => parseInt(key))
// OR Number(key)
console.log(keys) // Output [200, 768]
Do we need something like this?
var userId = 1;var myObject ={};
console.log( typeof userId ); // number
myObject[userId] = 'a value';
console.dir(myObject);
Console:
Object
1
:
"a value"
You can't, but you can always convert keys to a numbers
const data = { 15: "value", name: "Apple" };
const result = Object.keys(data) // get keys as an array
.map((item) => {
return parseInt(item); // convert to integer number
})
.filter((item) => !isNaN(item)); // remove non number elements
console.log(result); //Output: [15]
const a = {
'1': 'a value'
}
//by using a + before any string value it will convert(parse) that into a number
const b = Object.key(a);
console.log(+b); //parse
console.log(typeof b); //number
Per Mozilla:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax[Spread syntax]1
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) => ( { ...objects } );
let mergedObj1 = merge (obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }
let mergedObj2 = merge ({}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }
Just order the items before hand and you should get the result you want.
So for your case:
const merge = (...objects) => ({...objects});
//An object with numeric keys
const values = ["a value", "another value", "and another value"];
let merged = merge(...values);
console.log(merged);
You can try this:
arr = {}
function f(a,b,c) {
arr = arguments
}
f("*","#","_")
console.log(arr)
//returns Object { 0: "*", 1: "#", 2: "_" }```
In JavaScript, numerical strings and numbers are interchangeable, so
myObject[1] == myObject['1']
If you really want number to be the key for an object, you might want an array (i.e. created with new Array() or []).

Categories

Resources