detecting if object has children or not - actionscript-3

I have the following problem, i have to know if an element has children or not. if the object looks a followed:
Object [
Object[["name" : "lisa"], ["age" : "14"], ["gender" : "female"]],
Object[["name" : "bjorn"], ["age" : "40"], ["gender" : "male"]],
Object[["name" : "zoe"], ["age" : "24"], ["gender" : "female"]]
]
it should follow 1 route, if it looks as following:
Object[["name" : "lisa"], ["age" : "14"], ["gender" : "female"]]
it should follow another route. In general, the first example object is a collection of the second example object. so in other words:
if (example 1) {
...do this...
} else if (example 2) {
...do that...
}

Your syntax looks weird. The normal way of defining an object (eg hashmap) would be
var myObj:* = {}
//Or if it's an array, as in your case;
var myArr:Array = [{name: "lisa", age:14, gender:"female"}, {...etc}]
There's no easy way to see if an object has entries however. This is how I usually do it;
var hasEntries:Boolean = false;
for(var key:String in myObj) {
hasNodes = true;
break;
}

Related

Mapping over a const variable and returning to the value of an input [duplicate]

I'm trying to access a property of an object using a dynamic name. Is this possible?
const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"
There are two ways to access properties of an object:
Dot notation: something.bar
Bracket notation: something['bar']
The value between the brackets can be any expression. Therefore, if the property name is stored in a variable, you have to use bracket notation:
var something = {
bar: 'foo'
};
var foo = 'bar';
// both x = something[foo] and something[foo] = x work as expected
console.log(something[foo]);
console.log(something.bar)
This is my solution:
function resolve(path, obj) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : null
}, obj || self)
}
Usage examples:
resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// or even use array indexes
// (someObject has been defined in the question)
resolve("part.0.size", someObject)
// returns null when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})
In javascript we can access with:
dot notation - foo.bar
square brackets - foo[someVar] or foo["string"]
But only second case allows to access properties dynamically:
var foo = { pName1 : 1, pName2 : [1, {foo : bar }, 3] , ...}
var name = "pName"
var num = 1;
foo[name + num]; // 1
// --
var a = 2;
var b = 1;
var c = "foo";
foo[name + a][b][c]; // bar
Following is an ES6 example of how you can access the property of an object using a property name that has been dynamically generated by concatenating two strings.
var suffix = " name";
var person = {
["first" + suffix]: "Nicholas",
["last" + suffix]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"
This is called computed property names
You can achieve this in quite a few different ways.
let foo = {
bar: 'Hello World'
};
foo.bar;
foo['bar'];
The bracket notation is specially powerful as it let's you access a property based on a variable:
let foo = {
bar: 'Hello World'
};
let prop = 'bar';
foo[prop];
This can be extended to looping over every property of an object. This can be seem redundant due to newer JavaScript constructs such as for ... of ..., but helps illustrate a use case:
let foo = {
bar: 'Hello World',
baz: 'How are you doing?',
last: 'Quite alright'
};
for (let prop in foo.getOwnPropertyNames()) {
console.log(foo[prop]);
}
Both dot and bracket notation also work as expected for nested objects:
let foo = {
bar: {
baz: 'Hello World'
}
};
foo.bar.baz;
foo['bar']['baz'];
foo.bar['baz'];
foo['bar'].baz;
Object destructuring
We could also consider object destructuring as a means to access a property in an object, but as follows:
let foo = {
bar: 'Hello World',
baz: 'How are you doing?',
last: 'Quite alright'
};
let prop = 'last';
let { bar, baz, [prop]: customName } = foo;
// bar = 'Hello World'
// baz = 'How are you doing?'
// customName = 'Quite alright'
You can do it like this using Lodash get
_.get(object, 'a[0].b.c');
UPDATED
Accessing root properties in an object is easily achieved with obj[variable], but getting nested complicates things. Not to write already written code I suggest to use lodash.get.
Example
// Accessing root property
var rootProp = 'rootPropert';
_.get(object, rootProp, defaultValue);
// Accessing nested property
var listOfNestedProperties = [var1, var2];
_.get(object, listOfNestedProperties);
Lodash get can be used in different ways, the documentation lodash.get
To access a property dynamically, simply use square brackets [] as follows:
const something = { bar: "Foobar!" };
const userInput = 'bar';
console.log(something[userInput])
The problem
There's a major gotchya in that solution! (I'm surprised other answers have not brought this up yet). Often you only want to access properties that you've put onto that object yourself, you don't want to grab inherited properties.
Here's an illustration of this issue. Here we have an innocent-looking program, but it has a subtle bug - can you spot it?
const agesOfUsers = { sam: 16, sally: 22 }
const username = prompt('Enter a username:')
if (agesOfUsers[username] !== undefined) {
console.log(`${username} is ${agesOfUsers[username]} years old`)
} else {
console.log(`${username} is not found`)
}
When prompted for a username, if you supply "toString" as a username, it'll give you the following message: "toString is function toString() { [native code] } years old". The issue is that agesOfUsers is an object, and as such, automatically inherits certain properties like .toString() from the base Object class. You can look here for a full list of properties that all objects inherit.
Solutions
Use a Map data structure instead. The stored contents of a map don't suffer from prototype issues, so they provide a clean solution to this problem.
const agesOfUsers = new Map()
agesOfUsers.set('sam', 16)
agesOfUsers.set('sally', 2)
console.log(agesOfUsers.get('sam')) // 16
Use an object with a null prototype, instead of the default prototype. You can use Object.create(null) to create such an object. This sort of object does not suffer from these prototype issues, because you've explicitly created it in a way that it does not inherit anything.
const agesOfUsers = Object.create(null)
agesOfUsers.sam = 16
agesOfUsers.sally = 22;
console.log(agesOfUsers['sam']) // 16
console.log(agesOfUsers['toString']) // undefined - toString was not inherited
You can use Object.hasOwn(yourObj, attrName) to first check if the dynamic key you wish to access is directly on the object and not inherited (learn more here). This is a relatively newer feature, so check the compatibility tables before dropping it into your code. Before Object.hasOwn(yourObj, attrName) came around, you would achieve this same effect via Object.prototype.hasOwnProperty.call(yourObj, attrName). Sometimes, you might see code using yourObj.hasOwnProperty(attrName) too, which sometimes works but it has some pitfalls that you can read about here.
// Try entering the property name "toString",
// you'll see it gets handled correctly.
const user = { name: 'sam', age: 16 }
const propName = prompt('Enter a property name:')
if (Object.hasOwn(user, propName)) {
console.log(`${propName} = ${user[propName]}`)
} else {
console.log(`${propName} is not found`)
}
If you know the key you're trying to use will never be the name of an inherited property (e.g. maybe they're numbers, or they all have the same prefix, etc), you can choose to use the original solution.
I came across a case where I thought I wanted to pass the "address" of an object property as data to another function and populate the object (with AJAX), do lookup from address array, and display in that other function. I couldn't use dot notation without doing string acrobatics so I thought an array might be nice to pass instead. I ended-up doing something different anyway, but seemed related to this post.
Here's a sample of a language file object like the one I wanted data from:
const locs = {
"audioPlayer": {
"controls": {
"start": "start",
"stop": "stop"
},
"heading": "Use controls to start and stop audio."
}
}
I wanted to be able to pass an array such as: ["audioPlayer", "controls", "stop"] to access the language text, "stop" in this case.
I created this little function that looks-up the "least specific" (first) address parameter, and reassigns the returned object to itself. Then it is ready to look-up the next-most-specific address parameter if one exists.
function getText(selectionArray, obj) {
selectionArray.forEach(key => {
obj = obj[key];
});
return obj;
}
usage:
/* returns 'stop' */
console.log(getText(["audioPlayer", "controls", "stop"], locs));
/* returns 'use controls to start and stop audio.' */
console.log(getText(["audioPlayer", "heading"], locs));
ES5 // Check Deeply Nested Variables
This simple piece of code can check for deeply nested variable / value existence without having to check each variable along the way...
var getValue = function( s, context ){
return Function.call( context || null, 'return ' + s )();
}
Ex. - a deeply nested array of objects:
a = [
{
b : [
{
a : 1,
b : [
{
c : 1,
d : 2 // we want to check for this
}
]
}
]
}
]
Instead of :
if(a && a[0] && a[0].b && a[0].b[0] && a[0].b[0].b && a[0].b[0].b[0] && a[0].b[0].b[0].d && a[0].b[0].b[0].d == 2 ) // true
We can now :
if( getValue('a[0].b[0].b[0].d') == 2 ) // true
Cheers!
Others have already mentioned 'dot' and 'square' syntaxes so I want to cover accessing functions and sending parameters in a similar fashion.
Code jsfiddle
var obj = {method:function(p1,p2,p3){console.log("method:",arguments)}}
var str = "method('p1', 'p2', 'p3');"
var match = str.match(/^\s*(\S+)\((.*)\);\s*$/);
var func = match[1]
var parameters = match[2].split(',');
for(var i = 0; i < parameters.length; ++i) {
// clean up param begninning
parameters[i] = parameters[i].replace(/^\s*['"]?/,'');
// clean up param end
parameters[i] = parameters[i].replace(/['"]?\s*$/,'');
}
obj[func](parameters); // sends parameters as array
obj[func].apply(this, parameters); // sends parameters as individual values
I asked a question that kinda duplicated on this topic a while back, and after excessive research, and seeing a lot of information missing that should be here, I feel I have something valuable to add to this older post.
Firstly I want to address that there are several ways to obtain the value of a property and store it in a dynamic Variable. The first most popular, and easiest way IMHO would be:
let properyValue = element.style['enter-a-property'];
however I rarely go this route because it doesn't work on property values assigned via style-sheets. To give you an example, I'll demonstrate with a bit of pseudo code.
let elem = document.getElementById('someDiv');
let cssProp = elem.style['width'];
Using the code example above; if the width property of the div element that was stored in the 'elem' variable was styled in a CSS style-sheet, and not styled inside of its HTML tag, you are without a doubt going to get a return value of undefined stored inside of the cssProp variable. The undefined value occurs because in-order to get the correct value, the code written inside a CSS Style-Sheet needs to be computed in-order to get the value, therefore; you must use a method that will compute the value of the property who's value lies within the style-sheet.
Henceforth the getComputedStyle() method!
function getCssProp(){
let ele = document.getElementById("test");
let cssProp = window.getComputedStyle(ele,null).getPropertyValue("width");
}
W3Schools getComputedValue Doc This gives a good example, and lets you play with it, however, this link Mozilla CSS getComputedValue doc talks about the getComputedValue function in detail, and should be read by any aspiring developer who isn't totally clear on this subject.
As a side note, the getComputedValue method only gets, it does not set. This, obviously is a major downside, however there is a method that gets from CSS style-sheets, as well as sets values, though it is not standard Javascript.
The JQuery method...
$(selector).css(property,value)
...does get, and does set. It is what I use, the only downside is you got to know JQuery, but this is honestly one of the very many good reasons that every Javascript Developer should learn JQuery, it just makes life easy, and offers methods, like this one, which is not available with standard Javascript.
Hope this helps someone!!!
For anyone looking to set the value of a nested variable, here is how to do it:
const _ = require('lodash'); //import lodash module
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
Documentation: https://lodash.com/docs/4.17.15#set
Also, documentation if you want to get a value: https://lodash.com/docs/4.17.15#get
You can do dynamically access the property of an object using the bracket notation. This would look like this obj[yourKey] however JavaScript objects are really not designed to dynamically updated or read. They are intended to be defined on initialisation.
In case you want to dynamically assign and access key value pairs you should use a map instead.
const yourKey = 'yourKey';
// initialise it with the value
const map1 = new Map([
['yourKey', 'yourValue']
]);
// initialise empty then dynamically assign
const map2 = new Map();
map2.set(yourKey, 'yourValue');
console.log(map1.get(yourKey));
console.log(map2.get(yourKey));
demo object example
let obj = {
name: {
first_name: "Bugs",
last_name: "Founder",
role: "Programmer"
}
}
dotted string key for getting the value of
let key = "name.first_name"
Function
const getValueByDottedKeys = (obj, strKey)=>{
let keys = strKey.split(".")
let value = obj[keys[0]];
for(let i=1;i<keys.length;i++){
value = value[keys[i]]
}
return value
}
Calling getValueByDottedKeys function
value = getValueByDottedKeys(obj, key)
console.log(value)
output
Bugs
const getValueByDottedKeys = (obj, strKey)=>{
let keys = strKey.split(".")
let value = obj[keys[0]];
for(let i=1;i<keys.length;i++){
value = value[keys[i]]
}
return value
}
let obj = {
name: {
first_name: "Bugs",
last_name: "Founder",
role: "Programmer"
}
}
let key = "name.first_name"
value = getValueByDottedKeys(obj, key)
console.log(value)
I bumped into the same problem, but the lodash module is limited when handling nested properties. I wrote a more general solution following the idea of a recursive descendent parser. This solution is available in the following Gist:
Recursive descent object dereferencing
Finding Object by reference without, strings,
Note make sure the object you pass in is cloned , i use cloneDeep from lodash for that
if object looks like
const obj = {data: ['an Object',{person: {name: {first:'nick', last:'gray'} }]
path looks like
const objectPath = ['data',1,'person',name','last']
then call below method and it will return the sub object by path given
const child = findObjectByPath(obj, objectPath)
alert( child) // alerts "last"
const findObjectByPath = (objectIn: any, path: any[]) => {
let obj = objectIn
for (let i = 0; i <= path.length - 1; i++) {
const item = path[i]
// keep going up to the next parent
obj = obj[item] // this is by reference
}
return obj
}
You can use getter in Javascript
getter Docs
Check inside the Object whether the property in question exists,
If it does not exist, take it from the window
const something = {
get: (n) => this.n || something.n || window[n]
};
You should use JSON.parse, take a look at https://www.w3schools.com/js/js_json_parse.asp
const obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}')
console.log(obj.name)
console.log(obj.age)

Add new attribute to JSON

Using Node js and Sequelize ORM, i'm getting a data set. I need to add a new attribute to received data and send it to client side. This is what i tried.
Code Block 1
var varAddOns = { "id" : 5, "Name" : "Cheese"};
global.meal.findOne(
{
where: { id: 5 }
}).then(varMeal => {
var obj = {};
obj = varMeal;
obj.addons = varAddOns;
res.send(obj);
});
It returns a json like below. (Actually it does not contain "addons" data)
Code Block 2
{
"id": 12,
"mealName": "Burger",
"description": "Oily food",
}
but actually what i want is,
Code Block 3
{
"id": 12,
"mealName": "Burger",
"description": "Oily food",
"addons" : {
"id" : 5,
"Name" : "Cheese"
}
}
I tried something like below and it also wont work. (It returns same json as "Code Block 2'.)
Code Block 4
var newJson = {};
newJson = JSON.stringify(varMeal);
newJson['addons'] = varAddOns;
var retVal = JSON.parse(newJson);
res.send(retVal);
Can you help me to figure out, where the issue is?
EDIT
Code Block 5
var newJson = {};
newJson = varMeal;
newJson['addons'] = varAddOn;
var retVal = newJson;// JSON.parse(newJson);
res.send(retVal);
I tried 'Code block 5' as well. Same result comes out as 'Code block 2'. When I use JSON.parse(newJson), it was thrown an error. (Error is Unexpected token o in JSON at position 1)
You need to call .get on your model instance, and then attach extra properties to it:
var varAddOns = { "id" : 5, "Name" : "Cheese"};
global.meal.findOne(
{
where: { id: 5 }
}).then(varMeal => {
var obj = {};
obj = varMeal.get();
obj.addons = varAddOns;
res.send(obj);
});
A few things:
When you call findOne, Sequelize return a model instance, not a plain JS object with your data.
If you want to add extra properties to send to your user, you will first need to convert your model instance to a JS object with your data. You can do this by calling varMeal.get(). From there, you can add extra properties to it.
There is no need to prepend your variables with "var". It would be better to simply name your variable meal
you need the JSON to be an object when you are declaring newJson['addons'] as a nested object
Have you tried (in code block 4) not stringifying varMeal?

Combine JSON and String in a dictionary with Swifty

I'd like to create a JSON object in Swifty that has the form:
{
"store": {
"id": {
"test": "test"
},
"type": "retail",
"name": "store1"
}
}
Is there a way to combine types in a Dictionary to use with Swifty (String and JSON)? Quotes works, but when I try to assign a variable, it complains: Cannot assign value of type 'String' to type 'JSON?':
func jsonTest()->String {
var storeJson = [String: JSON]()
var someJson = JSON(["test":"test"])
storeJson["id"] = someJson
storeJson["type"] = "retail" // <-- works fine
var name = "store1"
storeJson["name"] = name // <-- Doesn't work
var store = JSON(storeJson)
return store.rawString()!
}
The reason
storeJson["type"] = "retail"
works differently than
storeJson["name"] = name
is because the first one follows a different path in the code. Specifically, it uses the init(stringLiteral value: StringLiteralType) method in the following extension (source).
extension JSON: Swift.StringLiteralConvertible {
public init(stringLiteral value: StringLiteralType) {
self.init(value)
}
public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
self.init(value)
}
public init(unicodeScalarLiteral value: StringLiteralType) {
self.init(value)
}
}
I'll explain further after we talk about how to fix your specific problem.
Possible solution #1:
storeJson["name"]?.string = name
Output:
{
"id" : {
"test" : "test"
},
"type" : "retail"
}
The reason
storeJson["name"]?.string = name
doesn't work as we might think is because of the optional chaining. Right now, if we ran this through the debugger, we wouldn't see anything meaningful. In fact, we would see nothing. This is a bit concerning and likely means storeJson["name"] is nil, so the statement is not executing any further. Let's verify our hypothesis by making it blow up. We'll change the line to:
storeJson["name"]!.string = name
In this case, with your current code, you'll likely get
fatal error: unexpectedly found nil while unwrapping an Optional value
as you should because storeJson["name"] is in fact nil. Therefore, this solution doesn't work.
Possible solution #2:
As you correctly noted in your answer, if you add a storeJson["name"] = JSON(name), you'll get the desired behavior:
func jsonTest()->String {
var storeJson = [String: JSON]()
var someJson = JSON(["test":"test"])
storeJson["id"] = someJson
storeJson["type"] = "retail" // <-- works fine
var name = "store1"
storeJson["name"] = JSON(name) // <-- works!
var store = JSON(storeJson)
return store.rawString()!
}
Output:
{
"id" : {
"test" : "test"
},
"name" : "store1",
"type" : "retail"
}
Great! Therefore, this solution works! Now, later in your code you can alter it however you want using .string and the like.
Explanation
Back to why the string literal works. You'll notice in the init, it has
self.init(value)
which passes through the objects init, which then goes through the case statement
...
case let string as String:
_type = .String
self.rawString = string
...
When you call storeJson["name"] = JSON(name), you're skipping the StringLiteralType init and simply going into the switch.
Therefore, you could interchange
storeJson["type"] = "retail"
with
storeJson["type"] = JSON("retail")
It turns out it works to change:
storeJson["name"] = name
to
storeJson["name"] = JSON(name)

AngularJS change property value based on another array

I have a json data structured as:
$scope.items = [{"Color" : "Red", "Size": "Small" }, { "Color" : "Orange" "Size": "Small"}, {"Color" : "Green" "Size": "Extra-Large"}];
then I have a properties json data:
$scope.properties = [{"PropertyName" : "Color", "FilteredAs" : "AllColors"}, {"PropertyName" : "Size", "FilteredAs" : "AllSizes"}]
I am doing a server-side filtering that's why there's a FilteredAs property in my properties json data. What I want to happen is:
If (items.key = properties.PropertyName)
example: "Color" (which is the first property in the items array) == "PropertyName" : "Color"
then that corresponding object's FilteredAs property in the properties array is "FilteredAs" : "AllColors", AllColors will be changed to "Red".
Any ideas on how to achieve this? Thanks!
EDIT: This is what I have for now:
angular.forEach($scope.items, function (value, key) {
for (var i = 0; i<= $scope.properties.length; i++) {
if ($scope.properties[i].PropertyName == $scope.items[value]) {
$scope.properties[i].SearchText = $scope.items[key];
}
}
If I understand it correctly as $scope.properties is an array you would need to foreach through the objects and identify the property that is for Color. Below should do what your looking for.
$scope.properties.forEach(function (property) {
if (property.PropertyName === "Color") {
property.FilteredAs = "Red" // Or whatever value or variable you have got back from webservice
}
});
Hope I have understood correctly and that this helps.
EDIT:
Because $scope.items is an array angular.forEach doesn't work that same way as it does for an object. You would need to change the code to be something like below.
angular.forEach($scope.items, function (item) {
for (var i = 0; i<= $scope.properties.length; i++) {
if ($scope.properties[i].PropertyName == "Color") {
$scope.properties[i].SearchText = item.Color;
}
}
With this though the value of $scope.properties[i].SearchText will always be the last Color in the $scope.items array. You might need to add some checked to ensure the right color is selected from the array with what your WebService says.

use ko.utils.arrayfilter to return collection based on value of inner collection

I am using knockout and want to use arrayFilter to return array of objects where a property "modified" inside of another array has value of true.
i.e.
my json object looks like
Family tree
Family{
LastName=Smith
Children{
Name=bob,
Modified=false},
{
Name=tom, Modified=true}
}
Family{
LastName=Jones
Children{
Name=bruno,
Modified=false},
{
Name=mary, Modified=false}
}
The result of the array filter would be (as follows) becuase child tom has modified =true
FamilyTree
Family{
LastName=Smith
Children{
Name=bob,
Modified=false},
{
Name=tom, Modified=true}
}
is this possible?
I think the solution that #pax162 supplied probably answers your question. However, there is the question of usage. The proposed solution is going to perform nested iterations. If you are only expecting to be processing the data once (as opposed to driving some rich client views), this is the approach to take. On the other hand, if you are binding this data to a view, you might consider another more KO-like approach. Here's what I have in mind:
var family = function(data){
var self = {
LastName :ko.observable(data.LastName),
Children : ko.observableArray( data.Children || [])
};
family.hasModifiedChildren = ko.computed(function() {
return ko.utils.arrayFirst(this.Children(),
function(child) {
return child.Modified === true;
}) !== null;
}, family);
return self;
}
Rather than using JSON data, create observable JS objects as such:
var families = return ko.utils.arrayMap(familiesJson, family);
// or, if you need an observable:
families = ko.observableArray(ko.utils.arrayMap(familiesJson, family));
Finally, get your list of families with modified children like this:
var familiesWithModifiedChildren = ko.computed(function() {
return ko.utils.arrayFilter(families, function(fam) {
return fam.hasModifiedChildren();
});
});
If you are building a live-update page, you'll want to go with this style of view model. This will allow Knockout to utilize its observer optimizations rather than building a new array every time the function is evaluated. Hope this helps!
If you want to get only families with at least one modified child, you can do this (http://jsfiddle.net/MAyNn/3/) . The json was not valid, changed it a bit.
var families = [
{
LastName :"Smith",
Children : [{
Name:"bob",
Modified:false},
{
Name:"tom", Modified :true}
]
},
{
LastName :"Jones",
Children : [{
Name:"bruno",
Modified:false},
{
Name:"mary", Modified :false}
]
}
];
var filteredFamilies = ko.utils.arrayFilter(families, function(family){
var hasModified = false;
var first = ko.utils.arrayFirst(family.Children, function(child){
return child.Modified;
})
if (first) {
hasModified = true;
}
return hasModified;
})
console.log(families);
console.log(filteredFamilies);