I have an object like this:
var obj = {
heroes: {
"1": {
label: "Spiderman"
},
"2": {
label: "Iron Man"
},
}
}
What I want to know it, whether there is an object e.g. 2 in obj.heroes.
I tried this, but it didn't work:
var name = "heroes"; //Will be a for-loop later
try {
if(name["2"] in obj)
console.log("There is an 2nd superhero!");
} catch(e) {console.log(e);}
..got only errors: "Cannot read property '2' of undefined"
I hope you are able to help me. Thanks
Try
if ("2" in obj[name]){
console.log("There is an 2nd superhero!");
}
But if you are trying to identify counts, it might be better if you used arrays
var obj = {
heroes: [
{label: "Spiderman"},
{label: "Iron Man"}
]
}
And check with
if (obj[name].length > 1) {
console.log("There is an 2nd superhero!");
}
You can do something like:
try {
console.log(obj.heroes["2"]);
} catch (e) {
console.log('nope :c');
}
However, it would be better to store heroes as an array:
var obj = {
heroes: [
{
label: 'Spiderman'
},
{
label: 'Ironman'
}
]
};
Using the array makes a little more sense since heroes consists of multiple hero objects.
If 2nd superhero doesn't exist the condition return false.
if(obj.heroes["2"])
console.log("There is an 2nd superhero!");
Or :
var count = 0;
for (var x in obj.heroes) {
if (obj.heroes.hasOwnProperty(x)) {
count++;
}
}
console.log("You see "+ count +" heroes.");
this code will hunt it down for you
var delve = function(object, property, dodge) {
if (!dodge) dodge = object;
for (var i in object) {
if (object[i] === dodge) continue;
if (typeof(object[i]) == typeof({})) this.delve(object[i], property, dodge)
if (i == property) console.log(object[i]);
}
}
delve(heroes,'2')
Related
Below is running code snippet for the javascript object and array.
I have one jsonObj and here the ResultElementLevel could be the array or
object.
According to I just put if else condition and compare if Array and 'object'.
My question is,How would it be possible without if else condition?
can we write one function which compare object and Array inside single if.
The jsonObj is populating dynamically.
Here it would be possible CHECK object is also come into the Array or Object.
var jsonObj = {
"Response": {
"Errors": {
"Check": {
"_attributes": {
"id": "51416",
"name": "lucyocftest090601"
},
"CheckLevel": {
},
"ResultElementLevel": {
"_text": "Line No (2) [Missing Reporting Category] "
}
}
},
"Success": {
}
}
}
iterateObjorArr(jsonObj);
function iterateObjorArr(jsonObj){
let checkArr = jsonObj.Response.Errors.Check;
let checkID = checkArr._attributes.id;
let checkName = checkArr._attributes.name;
let status = 'failed';
let resultElementLevel = checkArr.ResultElementLevel;
let errorUploadArr = [];
let errorUploadObj;
if (Array.isArray(resultElementLevel)) {
resultElementLevel.map(function (data, index) {
errorUploadObj = {
'id': checkID,
'checkName': checkName,
'status': status,
'errors/warnings': data._text
};
errorUploadArr.push(errorUploadObj);
});
} else {
if (typeof (resultElementLevel) === 'object') {
errorUploadObj = {
'id': checkID,
'checkName': checkName,
'status': status,
'errors/warnings': resultElementLevel._text
};
errorUploadArr.push(errorUploadObj);
}
}
console.log("errorUploadArr", errorUploadArr);
}
You can test to see if resultElementLevel has the length property or not using hasOwnProperty(). Arrays have a length while objects do not (generally):
if (resultElementLevel.hasOwnProperty('length')) {
// Handle it as an array
} else {
// Handle as an object
}
This will, however, only work if the object assigned to resultElementLevel is guaranteed to not have a length property.
My question is,How would it be possible without if else condition? can we write one function which compare object and Array inside single if.
I don't think you'd want to get rid of the condition, but being able to deal with the passed data the same way, wether it's an array, a single item, or null/undefined
You could normalize the data first
function toArray(value){
return value == null? []:
Array.isArray(value)? value:
//isArrayLike(value)? Array.from(value):
[value];
}
//Objects that look like Arrays
function isArrayLike(value){
return value !== null && typeof value === "object" && value.length === (value.length >>> 0);
}
so that from here on, you always deal with an Array:
let errorUploadArr = toArray(checkArr.ResultElementLevel)
.map(function(item){
return {
id: checkID,
checkName: checkName,
status: status,
"errors/warnings": item._text
};
});
var jsonObj = {
Response: {
Errors: {
Check: {
_attributes: {
id: "51416",
name: "lucyocftest090601"
},
CheckLevel: {},
ResultElementLevel: {
_text: "Line No (2) [Missing Reporting Category] "
}
}
},
Success: {}
}
};
iterateObjorArr(jsonObj);
function toArray(value) {
return value == null ? [] :
Array.isArray(value) ? value :
//isArrayLike(value)? Array.from(value):
[value];
}
//Objects that look like Arrays
function isArrayLike(value) {
return value !== null && typeof value === "object" && value.length === (value.length >>> 0);
}
function iterateObjorArr(jsonObj) {
let checkArr = jsonObj.Response.Errors.Check;
let checkID = checkArr._attributes.id;
let checkName = checkArr._attributes.name;
let status = "failed";
let errorUploadArr = toArray(checkArr.ResultElementLevel)
.map(function(data) {
return {
id: checkID,
checkName: checkName,
status: status,
"errors/warnings": data._text
}
});
console.log("errorUploadArr", errorUploadArr);
}
.as-console-wrapper{top:0;max-height:100%!important}
I'm working with a array of category objects that can have an array of child category objects. The tricky part is that the depth of this nested data is unknown (and can change). (See sample at bottom.) What I'm trying to do is return a "trail" to the category object but I'm having all sorts of difficulties.
Ideally something like findCategory('b4') would return: ['c1', 'd2', 'd3', 'b4'] (See sample).
I think my issue is I'm having trouble with properly breaking out of the nested loops caused by my recursion. Sometimes I'll get extra categories in my trail or when I think I've broken out, some deeper nested category ends up in the trail.
One result might look like this. Clearly it's not killing the loop at b4 and I'm not sure why the result is found twice.
b4
FOUND
["c1", "d2", "d3", "b4"]
e2
FOUND
["c1", "d2", "d3", "b4", "e2"]
Bonus if you can also show me an underscore.js version.
JSFiddle Link here...
// Start function
function findCategory(categoryName) {
var trail = [];
var found = false;
function recurse(categoryAry) {
for (var i=0; i < categoryAry.length; i++) {
console.log(categoryAry[i].category);
trail.push(categoryAry[i].category);
// Found the category!
if ((categoryAry[i].category === categoryName) || found) {
console.log('FOUND');
found = true;
console.log(trail);
break;
// Did not match...
} else {
// Are there children / sub-categories? YES
if (categoryAry[i].children.length > 0) {
console.log('recurse');
recurse(categoryAry[i].children);
// NO
} else {
trail.pop();
if (i === categoryAry.length - 1) {
trail.pop();
}
}
}
}
}
return recurse(catalog);
}
console.clear();
console.log(findCategory('b4'));
E.g. The array category objects, with nested array of category objects. Assume the depth of nesting is unknown.
var catalog = [
{
category:"a1",
children:[
{
category:"a2",
children:[]
},
{
category:"b2",
children:[
{
category:"a3",
children:[]
},
{
category:"b3",
children:[]
}
]
},
{
category:"c2",
children:[]
}
]
},
{
category:"b1",
children:[]
},
{
category:"c1",
children:[
{
category:"d2",
children:[
{
category:"c3",
children:[]
},
{
category:"d3",
children:[
{
category:"a4",
children:[]
},
{
category:"b4",
children:[]
},
{
category:"c4",
children:[]
},
{
category:"d4",
children:[]
}
]
}
]
},
{
category:"e2",
children:[
{
category:"e3",
children:[]
}
]
}
]
}
];
Try
function findCategory(categoryName) {
var trail = [];
var found = false;
function recurse(categoryAry) {
for (var i = 0; i < categoryAry.length; i++) {
trail.push(categoryAry[i].category);
// Found the category!
if ((categoryAry[i].category === categoryName)) {
found = true;
break;
// Did not match...
} else {
// Are there children / sub-categories? YES
if (categoryAry[i].children.length > 0) {
recurse(categoryAry[i].children);
if(found){
break;
}
}
}
trail.pop();
}
}
recurse(catalog);
return trail
}
Demo: Fiddle
the return stmt does work but remember it will be called everytime the loop unwinds and that's not what you are looking at. Example
// global scope
String matchingVariable;
int getMatch(index count, String input, String[] inputs){
if(isValid(input) || count < inputs.length){
// your condition is met and break
// assign your value to global scope variable
matchingVariable = input;
}else if(matchingVariable ==null){
++count
if(count < inputs.length){
getMatch(count, input+inputs[count], inputs)
}
// NOTE RETURN - I WOULDN'T DO THIS
return input;
// doesn't work instead assign the input to global scope variable when a match is found.
}
}
to begin with, I have a multilevel of entities as in
country unit ----> customer reporting group ----> customers
each country unit has different customer reporting groups and each of the later has different customers
in the code the variable names are
cu ----> crg ---> customer
this is represented in a multilevel object called menuData:
menuData = {
cu1: {
CRG3: {
Customer1: {},
Customer5: {}
},
CRG7: {
Customer3: {},
Customer2: {},
Customer7: {}
}
},
cu4: {
CRG1: {
Customer2: {},
Customer4: {}
},
CRG3: {
Customer4: {}
}
}
};
what I wanted to do is to construct unique id for each level in a multilevel objects as well as in for example the ids for the customer units will be the same
cu1 and cu2 and so on
for the customer reporting groups the ids will consist of the cu + the crg as in
cu1+crg4
for the customer:
cu1+crg4+customer6;
what I did is a function called getIds
var getIds = function(menuData) {
var ids = {};
for (cu in menuData) {
ids[cu] = cu;
for (crg in menuData[cu]) {
if (!(ids[cu] in ids)) {
ids[cu] = {};
ids[cu][crg] = ids[cu].concat(crg);
} else ids[cu][crg] = ids[cu].concat(crg);
for (customer in menuData[cu][crg]) {
if (!ids[cu][crg]) {
ids[cu][crg] = {};
ids[cu][crg][customer] = ids[cu][crg].concat(customer);
} else ids[cu][crg][customer] = ids[cu][crg].concat(customer);
}
}
}
console.log(ids);
return ids;
};
the error I got is
Cannot read property 'concat' of undefined
what I have tried is that, because it says that it's undefined, I try to define it if its not already defined as in
if (!(ids[cu] in ids)) {
ids[cu] = {};
ids[cu][crg] = ids[cu].concat(crg);
}
if its not defined, define it and insert the value, but if its defined, only assign the value
else ids[cu][crg] = ids[cu].concat (crg );
why do I get this error? and how to get the the ids in multilevel objects ?
edit, excpected output is
ids = {
"cu1": {
"cu1+CRG3": { "cu1+CRG3+Customer1":{}, "cu1+CRG3+Customer5":{} },
"cu1+CRG7": { "cu1+CRG7+Customer3":{}, "cu1+CRG7+Customer2":{}, "cu1+CRG7+Customer7":{} }
},
"cu4": {
"cu4+CRG1": { "cu4+CRG1+Customer2":{}, "cu4+CRG1+Customer4":{} },
"cu4+CRG3": { "cu4+CRG3+Customer4":{}}
}
}
The Problem with your Code is that you are using Objects to store your data and Objects don´t have the Method "concat" only Arrays have the "concat" Method. Your Object must look like these to work:
menuData = [
"cu1": [
"CRG3": [ "Customer1":{}, "Customer5":{} ],
"CRG7": [ "Customer3":{}, "Customer2":{}, "Customer7":{} ]
],
"cu4": [
"CRG1": [ "Customer2":{}, "Customer4":{} ],
"CRG3": [ "Customer4":{}]
]
]
Here´s a reference : MDN Array.concat()
What can be confusing in JS is that an Object Property can be accessed like an Array.
Update after Expected Output was added:
okay than i think concat is not the right solution for your Problem.
Try it with something like this:
var ids = {};
var menuData = {
cu1: {
CRG3: {
Customer1: {},
Customer5: {}
},
CRG7: {
Customer3: {},
Customer2: {},
Customer7: {}
}
},
cu4: {
CRG1: {
Customer2: {},
Customer4: {}
},
CRG3: {
Customer4: {}
}
}
};
for (propKeyLevel1 in menuData){
ids[propKeyLevel1] = {};
var propLevel1 = ids[propKeyLevel1];
for(propKeyLevel2 in menuData[propKeyLevel1]){
propLevel1[propKeyLevel1+"+"+propKeyLevel2] = {};
var propLevel2 = propLevel1[propKeyLevel1+"+"+propKeyLevel2];
for(propKeyLevel3 in menuData[propKeyLevel1][propKeyLevel2]){
propLevel2[propKeyLevel1+"+"+propKeyLevel2+"+"+propKeyLevel3] = {};
}
}
}
console.log(ids);
concat is a method for for a String or an Array, here you call it on an object hence the error.
What you're trying to do is a bit unclear to me, but maybe you could try that :
ids[cu][crg] = crg;
instead of :
ids[cu][crg] = ids[cu].concat (crg );
Because that's what you seem to be trying.
I’d try it this way:
function getIds(dataIn, idsIn) {
idsIn = idsIn || [];
var dataOut = {}, idOut;
for (var idIn in dataIn) {
idsOut = idsIn.concat([idIn]);
dataOut[idsOut.join('+')] = getIds(dataIn[idIn], idsOut);
}
return dataOut;
}
Perfect use case for a recursive function passing down an array (idsOut) of the ids of the previous layers to generate the intended object keys. Pretty straight forward.
I have an Javascript object like so...
var strategies = [{
"strategy": {
"category": "war"
}
}, {
"strategy": {
"category": "farming"
}
}]
I then have an array that indicates which results I'd like back. It can be any of the following: [] OR ["war"] ["farming"] OR ["war", "farming"].
If we have the [], I want to return no results. But if ["war", "farming"] I want to return both of the results above.
How do I accomplish this with Array.prototype.filter? I saw this post, but couldn't reason through it.
strategies.filter((strategy) =>
????
)
Thanks for your help.
You can just check the value with indexOf:
var categories = ['war', 'farming'];
var filtered = strategies.filter((obj) => {
return categories.indexOf(obj.strategy.category) > -1;
});
Your object, strategy was a wrapped object, so my first line was setting it to its inner strategy and then filter as needed.
var strategies = [{
"strategy": {
"category": "war"
}
}, {
"strategy": {
"category": "farming"
}
}]
var b = ["war", "farming"];
strategies.filter(function(strategy){
strategy = strategy.strategy;
for(var i in b){
if (b[i] == strategy["category"]) {
return true;
}
}
return false;
});
Tests the input array to see if it's empty as per your requirements:
function filterObj(arr) {
return !arr.length ? arr :
strategies.filter((el) => arr.indexOf(el.strategy.category) > -1);
}
filterObj(['war', 'farming'])
DEMO
I receive (in my angularjs application) from a server a list of directories like this:
['.trash-user',
'cats',
'cats/css',
'cats/images/blog',
'cats/images/gallery']
And I would like to build a javascript variable which looks like this:
[{
label: '.trash-user'},
{label: 'cats',
children: [{
label: 'css'},
{label: 'images',
children: [{
label: 'blog'},
{label: 'gallery'}
]}
]}
}]
The paths are in random order.
Hope somebody has some really elegant solution, but any solution is appreciated!
Edit:
Here is my naive approach, I have real trouble with recursion.
I could only make level 0 to work:
var generateTree = function(filetree){
console.log('--------- filetree -------');
var model = [];
var paths = [];
for(var i=0;i<filetree.length;i++) {
paths = filetree[i].split('/');
for(var j=0;j<paths.length;++j) {
var property = false;
for(var k=0;k<model.length;++k) {
if (model[k].hasOwnProperty('label') &&
model[k].label === paths[0]) {
property = true;
}
}
if (!property) {
model.push({label: paths[0]});
}
}
}
console.log(model);
};
If you want an elegant solution, lets start with a more elegant output:
{
'.trash-user': {},
'cats': {
'css': {},
'images': {
'blog': {},
'gallery': {},
},
},
}
Objects are much better than arrays for storing unique keys and much faster too (order 1 instead of order n). To get the above output, do:
var obj = {};
src.forEach(p => p.split('/').reduce((o,name) => o[name] = o[name] || {}, obj));
or in pre-ES6 JavaScript:
var obj = {};
src.forEach(function(p) {
return p.split('/').reduce(function(o,name) {
return o[name] = o[name] || {};
}, obj);
});
Now you have a natural object tree which can easily be mapped to anything you want. For your desired output, do:
var convert = obj => Object.keys(obj).map(key => Object.keys(obj[key]).length?
{ label: key, children: convert(obj[key]) } : { label: key });
var arr = convert(obj);
or in pre-ES6 JavaScript:
function convert(obj) {
return Object.keys(obj).map(function(key) {
return Object.keys(obj[key]).length?
{ label: key, children: convert(obj[key])} : { label: key };
});
}
var arr = convert(obj);
I'll venture that generating the natural tree first and then converting to the array will scale better than any algorithm working on arrays directly, because of the faster look-up and the natural impedance match between objects and file trees.
JSFiddles: ES6 (e.g. Firefox), non-ES6.
Something like this should work:
function pathsToObject(paths) {
var result = [ ];
// Iterate through the original list, spliting up each path
// and passing it to our recursive processing function
paths.forEach(function(path) {
path = path.split('/');
buildFromSegments(result, path);
});
return result;
// Processes each path recursively, one segment at a time
function buildFromSegments(scope, pathSegments) {
// Remove the first segment from the path
var current = pathSegments.shift();
// See if that segment already exists in the current scope
var found = findInScope(scope, current);
// If we did not find a match, create the new object for
// this path segment
if (! found) {
scope.push(found = {
label: current
});
}
// If there are still path segments left, we need to create
// a children array (if we haven't already) and recurse further
if (pathSegments.length) {
found.children = found.children || [ ];
buildFromSegments(found.children, pathSegments);
}
}
// Attempts to find a ptah segment in the current scope
function findInScope(scope, find) {
for (var i = 0; i < scope.length; i++) {
if (scope[i].label === find) {
return scope[i];
}
}
}
}