When working with nodejs, I like to use console.log to see what data is available in an object.
However, this doesn't work with inherited properties:
var Person = function () {};
Person.prototype.name = "anonymous";
var p = new Person();
console.log(['p', p]); // [ 'p', {} ]
// This doesn't even give me a hint that it's inherited from Person!
console.log(['typeof p', typeof p]); // [ 'typeof p', 'object' ]
console.log(['p.name', p.name]); // "anonymous"
Given an object, how can view all the properties I can access?
If your purpose is just for debugging, you can check the __proto__ object:
function Person() {};
Person.prototype.name = "abc";
Person.prototype.smallObj = {
name: "abc"
};
Person.prototype.deepObj = {
one: {
two: {
three: {
four: "4"
}
}
}
};
var p = new Person();
console.log(p);
// Person {}
console.log(p.__proto__);
/*
Person {
name: 'abc',
smallObj: { name: 'abc' },
deepObj: { one: { two: [Object] } }
}
*/
var util = require("util");
console.log(util.inspect(p.__proto__, {depth: null}));
/*
Person {
name: 'abc',
smallObj: { name: 'abc' },
deepObj: { one: { two: { three: { four: '4' } } } }
}
*/
On the last one, using util.inspect() with the depth option will allow you to look further into deeply nested objects.
You are assigning property to constructor function Person. It does not share properties with instances. You need to add property to Person's prototype:
Person.prototype.name = "anonymous";
To find out if your object inherited from Person you can do:
p instanceof Person; // true
You can print out all of an object's enumerable properties by performing the following:
for (var key in p) {
console.log(key);
}
Use Object.getOwnPropertyNames() to get all properties that belong to an object:
console.log(Object.getOwnPropertyNames(Person))
// [ 'length', 'name', 'arguments', 'caller', 'prototype' ]
console.log(Object.getOwnPropertyNames(Object))
// ['length','name','arguments','caller','prototype','keys','create', 'defineProperty','defineProperties','freeze','getPrototypeOf','setPrototypeOf','getOwnPropertyDescriptor','getOwnPropertyNames','is','isExtensible','isFrozen','isSealed','preventExtensions','seal','getOwnPropertySymbols','deliverChangeRecords','getNotifier','observe','unobserve','assign' ]
Also you can combine Object.getOwnPropertyNames() with walking up the prototype chain:
var getAllProperties = function (object) {
var properties = []
do {
Object.getOwnPropertyNames(object).forEach((prop) => {
if (!~properties.indexOf(prop)) {
properties.push(prop)
}
})
} while (object = Object.getPrototypeOf(object))
return properties
}
Related
I have some trouble in using spread operator.
when using spread operator, unexpected attribute exists in created object.
Can I filter only defined attribute or remove undefined attribute?
type test = { id: string }
const dump = { id: 'id', name: 'name' };
const testResult: test = { ...dump };
// wanted result { id: 'id' }
console.log('test', testResult); // test { id: 'id', name: 'name' }
I think the issue is not with respect to typescript alone here. Typescript is quite liberal in checking types. In the sense
interface IFoo {}
interface IBar {
name: string
}
// No error will be thrown
const foo: IFoo = { name: 'sam' };
const bar: IBar = { name: 'sam' };
// following line will throw error. As IBar expects the property name.
const justFoo: IBar = { greet: 'hello' };
Here foo can be used as a variable of one of types IFoo or IBar. Either will work. But it's not true in case of justFoo, it can only be IFoo.
This is how you should do it:
type test = { id: string }
const dump = { id: 'id', name: 'name' };
const testResult: test = {id:dump.id}
Say I have the following json files:
const obj1 = {
en: {
user: {
name: "John"
}
},
language: "en"
}
const obj2 = {
ru: {
user: {
name: "Vladimir"
}
},
language: "ru"
}
To retrieve these objects with NODE I will use the following code:
let en_name = obj1.en.user.name; //returns John
let ru_name = obj2.ru.user.name; //returns Vladimir
Is it possible to use the object language, and call the user.name based on the language? maybe something like this, which doesn't work:
let lang = obj.language
let anyName = language.user.name;
UPDATE:
I want to access data.jvProfiles.ANY_LANGUAGE.title as shown in the figure:
console.log(data.preferredLanguage); //Returns "nl"
let title = data.jvProfiles.nl.title; //Ruturns "Database Marketeer"
console.log(data.jvProfiles.data[data.preferredLanguage]); //gives error
console.log(data.jvProfiles.data[data.preferredLanguage].title); //gives same error
TypeError: Cannot read property 'nl' of undefined
SOLUTION:
console.log(data.jvProfiles[data.preferredLanguage].title);
You can use square brackets notation to access an object property dynamically:
const obj = {
en: {
user: {
name: "John"
}
},
language: "en"
}
const name = obj[obj.language].user.name
console.log(name)
I am trying to generate an object via a for loop, the problem I am having is that the property name is not being generated instead it is just inserted as the variable name.
Here is an example:
for (let key in person) {
let obj = {key : person[key] };
console.log(obj);
}
If you run this it prints
{ key : "smith" }
The desired object would be
{ name : "smith" }
any ideas on how to achieve this? thank you in advanced.
What you want is :
const person = {
age: 18,
size: '1m74',
eyeColor: 'blue',
};
for (let key in person) {
const obj = {
[key] : person[key],
};
console.log(obj);
}
Look at here for explainations
Example with Array.forEach and Object.keys
const person = {
age: 18,
size: '1m74',
eyeColor: 'blue',
};
Object.keys(person).forEach((x) => {
const obj = {
[x]: person[x],
};
console.log(obj);
});
You can achieve using
for (let key in person) {
const obj = {};
obj[key] = person[key];
console.log(obj);
}
You can do this by :
obj = {name: person[key] }
So I will be constantly retrieving an object with the following format:
student: {
"student_id": "12345",
"location": "below",
},
]
},
]
Thank you and will accept answer and upvote!
Something like this should do the trick:
var students = [];
function addStudent(student) {
// Check if we already know about this student.
var existingRecord = students.find(function (s) {
return s.student_id === student.student_id;
});
var classInfo = {
class_number: student.class_number,
location: student.location
};
if (!existingRecord) {
// This is the first record for this student so we construct
// the complete record and add it.
students.push({
student_id: student.student_id,
classes: [classInfo]
});
return;
}
// Add to the existing student's classes.
existingRecord.classes.push(classInfo);
}
You would then invoke it as follows:
addStudent({
"student_id": "67890",
"class_number": "abcd",
"location": "below",
});
Runnable JSBin example available here.
More available on Array.prototype.find at MDN.
This problem can be solved using indexing by student_id. For example:
var sourceArray = [{...}, {...}, ...];
var result = {};
sourceArray.forEach(function(student){
var classInfo = {
class_number: student.class_number,
location : student.location
};
if(result[student.student_id]){
result[student.student_id].classes.push(classInfo);
} else {
result[student.student_id] = {
student_id : student.student_id,
classes : [classInfo]
}
}
});
// Strip keys: convert to plain array
var resultArray = [];
for (key in result) {
resultArray.push(result[key]);
}
You can use also result format that contains objects, indexed by student_id or plain array resultArray.
I am building kind of multitenancy using sequelize.js. Technically I need to filter all queries by predefined column and dynamic value of the current context. General idea was to use defaultScope to filter out other contexts, something like:
var context = () => { return "some current context id"; }
connection.define('kid', {
firstName: Sequelize.STRING,
photoUrl: Sequelize.STRING,
context: {
type: Sequelize.STRING,
defaultValue: context // this part works, it accepts function
}
}, {
defaultScope: {
where: {
context: context // this does not work, it does not accept function and values is defined only once
}
}
});
However this does not work because defaultScope is defined on the application start.
What is the right way to do this?
The problem is that Sequelize scopes are defined on the model but you need to apply the scope just before the query because that's when you have context such as the user and role.
Here's a slightly modified copy of the scope merge function from Sequelize which you can use in your hooks such as beforeFind()
// Feel free to write a more fp version; mutations stink.
const {assign, assignWith} = require('lodash')
const applyScope = ({scope, options}) => {
if (!scope) {
throw new Error('Invalid scope.')
}
if (!options) {
throw new Error('Invalid options.')
}
assignWith(options, scope, (objectValue, sourceValue, key) => {
if (key === 'where') {
if (Array.isArray(sourceValue)) {
return sourceValue
}
return assign(objectValue || {}, sourceValue)
}
else if (['attributes', 'include'].indexOf(key) >= 0
&& Array.isArray(objectValue)
&& Array.isArray(sourceValue)
) {
return objectValue.concat(sourceValue)
}
return objectValue ? objectValue : sourceValue
})
}
In your model:
{
hooks: {
beforeFind(options) {
// Mutates options...
applyScope({
scope: this.options.scopes.user(options.user)
, options
})
return options
}
}
, scopes: {
user(user) {
// Set the scope based on user/role.
return {
where: {
id: user.id
}
}
}
}
}
Finally in your query, set an option with the context that you need.
const user = {id: 12, role: 'admin'}
YourModel.findOne({
attributes: [
'id'
]
, where: {
status: 'enabled'
}
, user
})
I'm not sure it will help, but you can override a model default scope anytime.
let defaultScope = {
where: {
context: ""
}
};
defaultScope.where.context = context();
model.addScope('defaultScope',defaultScope,{override: true});
Maybe too late here but scopes can take arguments if defined as functions. From documentation Sequelize scope docs if the scope is defined as
scopes: {
accessLevel (value) {
return {
where: {
accessLevel: {
[Op.gte]: value
}
}
}
}
sequelize,
modelName: 'project'
}
you can use it like: Project.scope({ method: ['accessLevel', 19]}).findAll(); where 19 is the dynamic value the scope will use.
As per defaultScope I'm not sure it can be defined as a function