I'm working on a java challenge that reads
//In the function below you'll be passed a user object. Loop through the user object checking to make sure that each value is truthy. If it's not truthy, remove it from the object. Then return the object. hint: 'delete'.
function truthyObjLoop(user) {
//code here
I came up with....
var user = {};
user.name = {};
user.age = {};
if (user !== false)
{
return user;
} else {
delete user;
}
}
However whenever I try it it comes back with the error Function returned
{"name":{},"age":{}}
instead of
{"name":"ernest","age":50}
when passed
{"name":"ernest","age":50,"funky":false}....
Can anyone help me understand why this is happening or if I'm using the wrong symbols here? Thank you.
var user = {};
user.name = {};
user.age = {};
The user name and age should be the values, not Objects. curly braces mentions objects, you are wrong.try my following code please
var user = {};
user.name = "john";
user.age = 12;
Also read this tutorial please :
http://www.w3schools.com/json/
The text says "you'll be passed a user object". That means the user is being defined outside of the function, and passed in. Here's what you're looking for:
function truthyObjLoop(user) {
for (var k in user) {
if (!user[k]) delete user[k]
}
console.log(user);
}
You then should pass in a "user" with all sorts of different attributes to demonstrate how the test works:
var person = {
age: 29,
gender: "male",
pets: [
{
type: "cat",
name: "smelly"
}
],
children: 0,
married: false
};
truthyObjLoop(person);
Here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/tb823pyp/
Notice it removes the attributes with a value of 'false' or '0'.
Related
Codesandbox to the problem:
https://codesandbox.io/s/serverless-thunder-6gj04?file=/src/store.js
I have the following store:
class PersonStore {
persons = [];
constructor() {
makeAutoObservable(this, {}, { autoBind: true });
}
*fetchPersons() {
const response = yield fetch(
"https://random-data-api.com/api/users/random_user?size=5"
);
this.persons = yield response.json();
}
}
export default new PersonStore();
Now, I want to update persons inside the person-list.
When I update a single field on on items inside the array like this it works as expected and the UI updates:
update(id) {
let updated = this.persons.find((p) => p.id === id);
// This works...
updated.first_name = "FOO";
}
However, in the future I would like to pass more complex, updated data into this function. So my idea was to basically assign a totally new object with the updated values in the list.
Unfortunately, this does not work as I expected it:
update(id) {
let updated = this.persons.find((p) => p.id === id);
// This does not work...
const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 }
updated = dummy_person
}
My first guess was that this does not work because the objects inside the array are not "normal objects" but observables. So I created a model for the persons:
class Person {
id = null;
first_name = "";
last_name = "";
constructor() {
makeAutoObservable(this);
this.first_name = "FOO";
this.last_name = "BAR";
}
}
...but this does still not work...
update(id) {
let updated = this.persons.find((p) => p.id === id);
// This does not work...
const dummy_person = new Person()
updated = person
}
How can I "replace" an object inside the array here with an object containing the updated data?
As #Tholle said in the comment, you are only updating local variable, not actual object.
In simpler words this is what's happening inside your update function:
let updated = this.persons.find((p) => p.id === id); - you create local variable updated and assign some object (person) to it
const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 } - you create dummy object and assign it to local dummy_person constant
updated = dummy_person - here you assign dummy_person value to updated variable. Basically you only reassign value of updated variable, you are not changing the person-object, but only changing to what value updated variable points.
Sometimes people explain it with boxes, like imagine that updated is a box and at first you put person inside of it and then changed you mind and put dummy_person inside, but actual person wasn't changed.
So what can you do?
As #Tholle said, you can use splice to change persons array, it will basically throw away old person and insert new one.
Or if you actually want to update old person you could use Object.assign(updated, dummy_person)
Or you could use map (although it will reassign whole array, might be unnecessary sometimes):
update(id) {
const dummy_person = { first_name: 'foo', last_name: 'bar', id: 99 };
this.persons = this.persons.map(person => person.id === id ? dummy_person : person);
}
There is a lot what you can do, depends on what you what. The main thing is understand how reactivity works!
More about this topic in the docs: https://mobx.js.org/understanding-reactivity.html
I have declared an object in JS and trying to assign a value to its properties.
But I can do it when only one property is defined, but not with more than one property.
This works fine:
let User = {
name
};
User['name']='Praveen';
alert(User.name);
But this does not
let User = {
name,
email
};
User['name']='Praveen';
User['email']='incopraveen#gmail.com';
alert(User.email); //says email is not defined.
NB: I have tried removing semicolons also.
Tried dot notation also
Because this:
let User = {
name,
email
};
is a shortform for:
let User = {
name: name,
email: email,
};
So it directly initializes both properties to the value that the variables name and email are holding. name is defined, it is the name of the page you are in, which you can easily check with:
console.log(name);
but email is not defined yet, and trying to get an undeclared variable results in an error:
console.log(email); // email is not defined
To solve that, explicitly declare both variables before:
let name = "test";
let email = "test#example.com";
let User = {
name,
email
};
Or initialize the properties not at all:
let User = {};
or directly set the properties to a value:
let User = {
name: "test",
email: "test#example.com",
};
Your code is ok,
Please check do you have any existing name,email variable which you are set in the User Object,
I think you do not have existing name and email variable. So that It can not create the User Object itself.
You can do like this..
let User = {};
User['name']='Praveen';
User['email']='incopraveen#gmail.com';
This link could help you, https://alligator.io/js/object-property-shorthand-es6/
Given the following javascript object:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
return this.key; //I want this to return "back" (the prop name)
},
desc: "goes back"
}
}
How can i access the Property Name which is "back" from within the action()?
I think it should be pretty simple, but if it isn't something simple than I'll add more details.
NOTE: aliases[0] is holding the name by chance, and it is not promised to hold it in the future or in other commands.
EDIT:
Sometimes we get to complicated while we can solve the problem pretty fast.
In this case i can just go ahead and return the string "back"
I'll leave the question and accept the answer that solves my question if there is such a solution.
Returning the string as you mentioned is definitely the easiest way. But I could see cases where someone might want to be able to get similar functionality with a dynamically created object in which the keys are not known until run-time.
A solution that would work in that case is exposing the commands object to the sub objects, so they can look themselves up:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === this){
return commandKeys[i];
}
}
},
desc: "goes back"
}
};
In this case it may also make more sense to share the function across all those action objects:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: getAction,
desc: "goes back"
},
forward: {
//...
action: getAction,
//...
}
};
function getAction() {
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === this){
return commandKeys[i];
}
}
}
Unless you need to perform some specific logic for each sub object.
EDIT: To improve efficiency, we can make it where the getAction function is not executed every call and add a property that will store the name. That way the lookup only occurs the first time.
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: getAction,
desc: "goes back"
},
forward: {
//...
action: getAction,
//...
}
};
// Only needs to getKey the first time called.
function getAction() {
if(!this.key) this.key = getKey(this);
return this.key;
}
function getKey(obj) {
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === obj){
return commandKeys[i];
}
}
}
When you call action as the following:
commands.back.action();
the scope of action is back. Sadly, the creation of the object that gets assigned to commands.back does not know that this inside of action is called "back". From my understanding, this is done because we could assign the object assigned to commands.back to another object with another name. As in:
var foo = { f: function(){console.log(this) } };
var bar = foo;
bar.f();
Or closer to what you have...
var foo = {
bar: {
f:function(){console.log(this)}
}
};
var other = { another: (foo.bar) };
The only way I know of where the object knows the name of what it was created within are functions. So, we can create a temp function that has the name back that will create an object as desired.
var commands = {
back:(new function back(){
// I prefer to assign to a variable to assist with the readability as to what "this" is:)
var self = this;
self.command = "b";
self.aliases = ["back","go back","backwards"];
self.action = function(){
// Can leave as "this" or change to "self".
return this.key;
};
self.desc = "goes back";
self.key = self.prototype.constructor.name;
})
}
Simplest Solution
But at that point might as well just add a property that already has the name. I would recommend doing a property called key or name rather than placing the name directly into the action function to make it easier to have multiple places where the name is used. Also, allows there to be a single place to change the name within the object if need be.
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
return this.key;
},
desc: "goes back",
key: "back"
}
}
EDIT: Added this edit as another way to do this, but I would still do the previous way. We can utilize Object.keys to get the name of the property since back is being added as an enumerable property of commands.
var i = 0,
commands = { back: {
key: (function(id){return function(){return Object.keys(commands)[id]}})(i++)
}}
Then can get the key by the following:
commands.back.key();
Or within the action function as:
this.key();
Can add key to back as a get which would look like:
var i = 0,
commands = { back: {
id: (i++),
get key() {return Object.keys(commands)[this.id]}
}}
This will allow you to access the property as commands.back.key and within the action function as this.key.
Can also pre-define everything then can do the following:
var i = 0, commands = { back: undefined };
commands.back = { key: Object.keys(commands)[i++] };
You can add and also advisable to add a toString method for your every object like this.
var commands = {
back:{
command: "b",
name : "back",
aliases: ["back","go back","backwards"],
action: function(){
return this.toString();
},
desc: "goes back",
toString : function(){
return this.name;
}
}
}
console.log(commands.back.action()); // back
console.log(commands.back.toString()); // back
What you are having here, is a nested object, held on the property of an object.
You can not get that property by hand - unless you are doing some strange metaprogramming stuff, such as getting the AST parent node and trying to determine the property the object is held etc. The easiest way, is to hold the property name using a string i.e.: "back".
In simple terms, it is like holding the object to a var
var obj = {/*....*/};
And you are trying to get the var name from within the object.
Remember though that in JavaScript, you can access an object property, using both string and index notation, so commands.back can also be called using commands['back']. If I am guessing right, you are trying to make a sort of dispatching, so this notation can be useful for you.
If you've got that object (literal object) you can't use this keyword. You have two solutions:
return commands.back.aliases[0]
Or otherwise,you can construct the object as a prototype object and not literal object:
var commands = function() {
this.back = function() {
this.command = "b";
this.aliases = ["back","go back","backwards"];
this.action = function() {
return this.aliases[0];
};
this.desc = "goes back";
};
};
And initialize like this
var instance = new commands();
instance.action(); // it returns "back" string
Is this the correct way to make a javascript class with default properties (even though some here are null), yet also having the option to add a creation argument that can contain specific values for some properties listed in object format/json?
Also should i put any of this into Person's prototype to save memory if I am creating many Person objects?
this is working for me but I wonder if it is a good way to do this ?
// PERSON Class -----------------------------------------------
QM.Person=function(data)
{
/* Closure for this */
var my = this;
this.PersonID=null;
this.Name_Last="";
this.Name_First="";
this.Date_Birth=null;
this.Biography="";
this.loadData=function(obj) {
for (var i in obj) {
if (my.hasOwnProperty(i)) {
my[i] = obj[i];
}
}
}
this.loadData(data);
}
example of creating using this class:
jondoe = new Person();
bob = new Person({Name_First:"Bob",Name_Last:"Bar"});
jondoe.Name_First /* "" */
bob.Name_First /* "Bob" */
To avoid repeating yourself you could build a list of keys you expect and also the default values to insert if they aren't specified:
MyType = function(data){
var keys = this.keys,
data = data || {},
key
for (key in keys) {
if (keys.hasOwnProperty(key)) {
if (data.hasOwnProperty(key)) {
this[key] = data[key];
} else {
this[key] = keys[key];
}
}
}
};
MyType.prototype.keys = {
personId: null,
firstName: 'A default first name',
lastName: ''
};
var o1 = new MyType();
var o2 = new MyType({
personId: 1,
firstName: 'Override'
});
console.log(o1, o2);
I wouldn't do it that way. I would do
QM.Person=function(data)
{
data = data || {};
this.PersonID= data.PersonId || null;
this.Name_Last= data.Name_Last || "";
...
}
You will note I am assuming you care about the explicit type, i.e. when you assign null, you want null, and not undefined. Same thing for "".
I think this is just simpler than creating a function and executing it to clean up your data
Finally, all methods your want to add to the function should be on the prototype. Properties should NOT be on the prototype, unless you want all instances to share the same properties. Since you don't have any methods other than your data-cleaner, nothing you have shown should go on the prototype.
I'm in the process of truly learning the nuances of working with JavaScript objects and ran into a snag.
I have a set of "namespaced" objects to segment the DOM and Model to act on. Below is code:
function Sandbox2(){
this.page = {
FirstName: document.getElementById("FirstName")
, LastName: document.getElementById("LastName")
, Email: document.getElementById("Email")
};
this.model = {
FirstName: "James"
, LastName: "Eggers"
, Email: "James.R.Eggers#gmail.com"
};
this.behavior = {};
this.bindPageToModel = function(){
for(var property in this.page){
if (property){
property.value = this.model[property];
}
}
};
this.bindModelToPage = function(){
for(var property in this.model){
if (property){
this.model[property].value = this.page[property];
}
}
};
};
Using JsTestDriver, I'm doing a number of tests to play around and try out a few things of the page and model objects. The specific test is below:
"test ModelBinding should be allowed." : function(){
var sandbox2 = new Sandbox2();
var page = sandbox2.page;
page.FirstName = "Test";
page.LastName = "Account";
sandbox2.bindModelToProperty();
assertEquals("Ensure the new values took.", page.FirstName, sandbox2.page.FirstName);
assertEquals("New Page values should be in the model.", "Test", sandbox2.model.FirstName);
}
In the above test, the first assertEquals passes; however, the second test resolves sandbox2.model.FirstName to "James" (the initial value).
Anyone have any recommendations on how I can change the code (original or test) to allow me to map the page object's values to the model object?
It seems like the issue is here:
for(var property in this.page){
if (property){
property.value = this.model[property];
}
}
The property variable is actually the key value of the object (FirstName, LastName and Email). You're setting the value attributes on these string objects without any result.
I think you meant to do something like:
this.page[property].value = this.model[property];