TypeError: Cannot read property - javascript

I am getting the following error:
2015-05-06T15:51:43.332-0700 map reduce failed:{
"errmsg" : "exception: TypeError: Cannot read property 'Dockey' of undefined near 'essaged[0].Dockey) { return; ' ",
"code" : 16722,
"ok" : 0
} at src/mongo/shell/collection.js:1224
Here is the script I am running it is a Map Reduce script run against mongo db.
function mapFunction() {
if (!this.eventdata || !this.eventdata.Messaged || this.eventdata.Messaged.length === 0 || !this.eventdata.Messaged[0].Dockey) {
return;
}
emit({
entityid: this.entityid,
eventType: this.eventType,
profileid: (this.eventdata.Messaged[0].Dockey || this.eventdata.Messaged[0].PID)
}, {
datecreated: this.datecreated
});
}
function reduceFunction(key, values) {
var reducedValue = values[0],
i;
for (i = 1; i < values.length; i++) {
if (values[i].datecreated > reducedValue.datecreated) {
reducedValue = values[i];
}
}
return reducedValue;
}
var res = db.TRACKING_DATA.mapReduce(mapFunction,
reduceFunction, {
out: {
reduce: "LASTCONNECTED_ALLTYPES"
},
query: {
datecreated: {
$gt: ISODate("2015-03-11T18:00:00.000Z")
}
},
}
)
Here is a doc entry from the collection:
"_id": "0003e88f-37ed-493c-930e-f401821faca1",
"_class": "com.daoservice.model.TrackingData",
"modified": ISODate("2015-03-03T22:00:04.679Z"),
"eventtype": "#Connect button clicked and default email tab opened",
"eventdata": {
"Messaged": [{
"ViewedID": "4918faab-2eca-4b36-8ca7-342f064a4699",
"Dockey": "ad30d3c827e2f1f44d4bae598ac14093",
"PID": "",
"UID": "",
"connectDate": ISODate("2015-03-03T22:00:04.676Z"),
"connectID": "fcd6d2cb-1f2d-43ac-b42d-deec7f5d7931",
"urlClicked": "",
"email": ""
}]
},
"eventsource": "Customer App",
"sourceip": "210.7.77.202",
"entityid": "1652430",
"groupid": "15013",
"datecreated": ISODate("2015-03-03T22:00:04.674Z")

The Messaged array has no members, but you're trying to directly access one in your last condition check !this.eventdata.Messaged[0].Dockey. Validate that the Messaged array has the member you're trying to access before doing so.
!this.eventdata.Messaged[0]
And your final condition should look like
if (!this.eventdata || !this.eventdata.Messaged || this.eventdata.Messaged.length === 0 || !this.eventdata.Messaged[0] || !this.eventdata.Messaged[0].Dockey) {

Related

parse array of object that contains JSON elements

First let me break down the data:
I have an array that contains 3 elements...
Each Element is an object with name and arrayOfJSON as keys...
Inside arrayOfJSON there could be any number of JSON strings as elements...
I need to capture the position where Alex#gmail occurs for both the array mess and arrayOfJSON
Result Should Be:
position_of_mess = [0,2]
position_of_arrayOfJSON_for_position_of_mess_0 = [0]
position_of_arrayOfJSON_for_position_of_mess_2 = [1]
What I'm trying at the moment:
For loop through mess, for loop through arrayOfJSON , and JSON.parse() for Alex#gmail.
going to take me a few mins to update.
If y'all think it can be done without a for-loop let me know.
Update: almost there
mess = [{
"name": "user1",
"arrayOfJSON": `[{"email":"Alex#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
},
{
"name": "user2",
"arrayOfJSON": `[{"email":"Chris#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
},
{
"name": "user3",
"arrayOfJSON": `[{"email":"bob#gmail","hobby":"coocking"},{"email":"Alex#gmail","hobby":"coding"}]`
}
]
console.log(mess)
for (i = 0; i < mess.length; i++) {
console.log(JSON.parse(mess[i].arrayOfJSON))
for (m = 0; m < (JSON.parse(mess[i].arrayOfJSON)).length; m++) {
console.log("almost")
console.log((JSON.parse(mess[i].arrayOfJSON))[m])
}
}
mess = [{
"name": "user1",
"arrayOfJSON": `[{"email":"Alex#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
},
{
"name": "user2",
"arrayOfJSON": `[{"email":"Chris#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
},
{
"name": "user3",
"arrayOfJSON": `[{"email":"bob#gmail","hobby":"coocking"},{"email":"Alex#gmail","hobby":"coding"}]`
}
]
console.log(mess)
holdMessPosition = []
for (i = 0; i < mess.length; i++) {
var pos = (JSON.parse(mess[i].arrayOfJSON)).map(function(e) {
return e.email;
})
.indexOf("Alex#gmail");
console.log("user position is " + pos);
if (pos !== -1) {
holdMessPosition.push(i)
}
}
console.log(holdMessPosition)
Parse your data
You want to be able to access keys inside the inner object "string"
Traverse your data
While visiting key-value pairs, build a scope thet you can later return
// Adapted from: https://gist.github.com/sphvn/dcdf9d683458f879f593
const traverse = function(o, fn, scope = []) {
for (let i in o) {
fn.apply(this, [i, o[i], scope]);
if (o[i] !== null && typeof o[i] === "object") {
traverse(o[i], fn, scope.concat(i));
}
}
}
const mess = [{
"name": "user1",
"arrayOfJSON": `[{"email":"Alex#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
}, {
"name": "user2",
"arrayOfJSON": `[{"email":"Chris#gmail","hobby":"coding"},{"email":"bob#gmail","hobby":"coocking"}]`
}, {
"name": "user3",
"arrayOfJSON": `[{"email":"bob#gmail","hobby":"coocking"},{"email":"Alex#gmail","hobby":"coding"}]`
}];
// Parse...
mess.forEach(item => {
if (item.arrayOfJSON) {
item.arrayOfJSON = JSON.parse(item.arrayOfJSON);
}
});
traverse(mess, (key, value, scope) => {
if (value === 'Alex#gmail') {
console.log(
`Position: mess[${scope.concat(key).map(k => isNaN(k) ? `'${k}'` : k).join('][')}]`
);
}
});
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}

PokeAPI + Angular: How to get pokemon's evolution chain

I am an Angular novice and am learning a little by trying to pull the evolution chain for each pokemon using pokeapi but having a difficult time because of deep nesting.
A typical response object is returned like this:
{
"baby_trigger_item": null,
"id": 2,
"chain": {
"evolution_details": [],
"evolves_to": [
{
"evolution_details": [
{
"min_level": 16,
"min_beauty": null,
"time_of_day": "",
"gender": null,
"relative_physical_stats": null,
"needs_overworld_rain": false,
"turn_upside_down": false,
"item": null,
"trigger": {
"url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
"name": "level-up"
},
"known_move_type": null,
"min_affection": null,
"party_type": null,
"trade_species": null,
"party_species": null,
"min_happiness": null,
"held_item": null,
"known_move": null,
"location": null
}
],
"evolves_to": [
{
"evolution_details": [
{
"min_level": 36,
"min_beauty": null,
"time_of_day": "",
"gender": null,
"relative_physical_stats": null,
"needs_overworld_rain": false,
"turn_upside_down": false,
"item": null,
"trigger": {
"url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
"name": "level-up"
},
"known_move_type": null,
"min_affection": null,
"party_type": null,
"trade_species": null,
"party_species": null,
"min_happiness": null,
"held_item": null,
"known_move": null,
"location": null
}
],
"evolves_to": [],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/6/",
"name": "charizard"
}
}
],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/5/",
"name": "charmeleon"
}
}
],
"is_baby": false,
"species": {
"url": "http://pokeapi.co/api/v2/pokemon-species/4/",
"name": "charmander"
}
}
}
I have to get to evolves_to property, and grab species.name as well as evolution_details.min_level and evolution_details.trigger.name, and evolution_details.item if not null
But as you can see, the evolves_to property, itself contains another evolves_to nested inside, which has another nested inside
This is my sad attempt (after http.get) and I'm just stuck now.
var evoObject = response.data;
function loopEvo(obj){
angular.forEach(obj, function(value, key, object){
if (key == 'evolves_to' && value != []) {
//from here I can get top level data, but...
}
});
}
loopEvo(evoObject.chain);
I don't know how to recursively dive into objects and continually grab data, can anyone provide any help? I would love to use this as a great learning opportunity in traversing complex json objects.
You could always just avoid using Angular and stick with plain JS to build out your evolution chain... try giving this a go, it was based on your angular for loop. This should leave you with an array (evoChain) of the objects containing the data you are looking for ordered from first evolution at 0 index to last evolution at the last index.
var evoChain = [];
var evoData = response.data.chain;
do {
var evoDetails = evoData['evolution_details'][0];
evoChain.push({
"species_name": evoData.species.name,
"min_level": !evoDetails ? 1 : evoDetails.min_level,
"trigger_name": !evoDetails ? null : evoDetails.trigger.name,
"item": !evoDetails ? null : evoDetails.item
});
evoData = evoData['evolves_to'][0];
} while (!!evoData && evoData.hasOwnProperty('evolves_to'));
In your sample case above the resulting array should appear as follows:
[{
"species_name": "charmander",
"min_level": 1,
"trigger_name": null,
"item": null
}, {
"species_name": "charmeleon",
"min_level": 16,
"trigger_name": "level-up",
"item": null
}, {
"species_name": "charizard",
"min_level": 36,
"trigger_name": "level-up",
"item": null
}]
The approved answer above does not work if there are multiple evolutions such as Eevee or Snorunt. That will only return the first evolution e.g. Vaporeon
The following checks number of evolutions and runs through them all.
let evoChain = [];
let evoData = chain.chain;
do {
let numberOfEvolutions = evoData['evolves_to'].length;
evoChain.push({
"species_name": evoData .species.name,
"min_level": !evoData ? 1 : evoData .min_level,
"trigger_name": !evoData ? null : evoData .trigger.name,
"item": !evoData ? null : evoData .item
});
if(numberOfEvolutions > 1) {
for (let i = 1;i < numberOfEvolutions; i++) {
evoChain.push({
"species_name": evoData.evolves_to[i].species.name,
"min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
"trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
"item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
});
}
}
evoData = evoData['evolves_to'][0];
} while (!!evoData && evoData.hasOwnProperty('evolves_to'));
return evoChain;
brandudno is correct: the extra if(numberOfEvolutions) is the more complete approach (THANKS #brandudno! This really helped me solve for ALL the use cases - including eevee!)
I like the use of !!evoData in the while statement now that I took the time to understand it, but it was confusing for me at 1st, so I made a minor modification that still works and may be easier for other new developers (continues until evoData becomes undefined).
Lastly, I made a minor change in case others also prefer to use the . annotation (evoData.evolves_tovs.evoData['evolves_to']`) to take advantage of autocomplete, etc.
let evoChain = [];
let evoData = chain.chain;
do {
let numberOfEvolutions = evoData.evolves_to.length;
evoChain.push({
"species_name": evoData .species.name,
"min_level": !evoData ? 1 : evoData .min_level,
"trigger_name": !evoData ? null : evoData .trigger.name,
"item": !evoData ? null : evoData .item
});
if(numberOfEvolutions > 1) {
for (let i = 1;i < numberOfEvolutions; i++) {
evoChain.push({
"species_name": evoData.evolves_to[i].species.name,
"min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
"trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
"item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
});
}
}
evoData = evoData.evolves_to[0];
} while (evoData != undefined && evoData.hasOwnProperty('evolves_to'));
return evoChain;
I am using a recursive function to solve this.
Here's how it goes with plain JavaScript.
let evoChain = [];
function getEvo(arr) {
if (arr[0].evolves_to.length > 0) {
evoChain.push(arr[0].species.name);
getEvo(arr[0].evolves_to);
} else {
evoChain.push(arr[0].species.name);
return 0;
}
}
getEvo([data.chain]);```

Filter the nested array based on condition

I have an object like this:
runs =
{
"suites": [
{
"status": "fail",
"testcases": [
{
"status": "pass"
},
{
"status": "fail"
}
]
},
{
"status": "pass",
"testcases": [
{
"status": "pass"
}
]
}
]
}
I would like to retrieve the count of test case pass and fail (In the above case pass: 2, fail: 1). I tried the following to get the pass count:
runs.suites.filter(suite => {
suite.testcases.filter(testcase => {
return testcase.status === 'pass';
})
}).length
But it is giving me an incorrect result.
Try this
// Code goes here
runs =
{
"suites": [
{
"status": "fail",
"testcases": [
{
"status": "pass"
},
{
"status": "fail"
}
]
},
{
"status": "pass",
"testcases": [
{
"status": "pass"
}
]
}
]
};
var count = 0;
var test = runs.suites.filter(suite => {
suite.testcases.filter(testcase => {
(testcase.status=='pass')?count++:count;
})
return count;
});
console.log(test.length);
You could use an object for counting.
var runs = { "suites": [{ "status": "fail", "testcases": [{ "status": "pass" }, { "status": "fail" }] }, { "status": "pass", "testcases": [{ "status": "pass" }] }] },
count = {}
runs.suites.forEach(a =>
a.testcases.forEach(b =>
count[b.status] = (count[b.status] || 0) + 1));
console.log(count);
Try this:
var runs = {"suites": [{"status": "fail","testcases": [{"status": "pass"},{"status": "fail"}]},{"status": "pass","testcases": [{"status": "pass"}]}]};
const statuses = runs.suites.reduce((a, b)=> a.testcases.concat(b.testcases)).map(x=> x.status)
console.log(statuses.filter(x=> x === "pass").length) // 2
console.log(statuses.filter(x=> x === "fail").length) // 1
You can use Array#reduce and take the count of pass/fail in an object {pass : 2, fail : 1}
var runs = {"suites": [{"status": "fail","testcases": [{"status": "pass"},{"status": "fail"}]},{"status": "pass","testcases": [{"status": "pass"}]}]};
var obj = runs.suites.reduce( (tot, curr)=> {
curr.testcases.forEach(tc=>{
tc.status === 'pass' ? tot.pass++ : tot.fail++;
});
return tot;
}, {pass : 0, fail : 0});
console.log(obj);

combining my objects and getting expected results

I am trying to merge my objects and get a result like below
{
"sports": {
"basketball": "kobe",
"swimming": {
},
"football": "ronaldo",
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
}
Somewhere I am doing wrong in my logic can you help me out
but if I alternate my sportA and sportsB value I am getting expected
results...not sure what problem in my current scenario
providing my code below.
fiddle
https://jsfiddle.net/tjLk0frq/3/
var sportsA ={
"sports": {
"basketball": "kobe",
"football": "ronaldo"
}
};
var sportsB ={
"sports": {
"basketball": "",
"swimming": {
},
"football": "",
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
};
function merge(sportsA, sportsB) {
for( var p in sportsB )
if( sportsA.hasOwnProperty(p) )
sportsA[p] = typeof sportsB[p] === 'object' ? merge(sportsA[p], sportsB[p]) : sportsB[p];
return sportsA;
}
merge(sportsA, sportsB );
console.log("unexpected result" + sportsA );
console.log( sportsA );
//expected
/*
{
"sports": {
"basketball": "kobe",
"swimming": {
},
"football": "ronaldo",
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
}
*/
You can use jQuery's extend method, with deep merge enabled, to do this:
var output = $.extend(true, sportsB, sportsA)
outputs:
{
"sports": {
"basketball": "kobe",
"swimming": {},
"football": "ronaldo",
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
}
You have mistake when you check sportsA.hasOwnProperty(p) in your case you only update properties that are in sportsA, but not add new from sportsB.
Also if sportsB[p] has falsy value you don't want to update it for that I've used (sportsB[p] || sportsA[p]).
Check this code.
var sportsA ={
"sports": {
"basketball": "kobe",
"football": "ronaldo"
}
};
var sportsB ={
"sports": {
"basketball": "",
"swimming": {
},
"football": "",
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
};
function merge(sportsA, sportsB) {
for( var p in sportsB )
if( sportsA.hasOwnProperty(p) ) {
sportsA[p] = typeof sportsB[p] === 'object' ? merge(sportsA[p], sportsB[p]) : (sportsB[p] || sportsA[p]);
} else {
sportsA[p] = sportsB[p];
}
return sportsA;
}
merge(sportsA, sportsB );
console.log("unexpected result" + sportsA );
console.log( sportsA );
Here you go (pure JS):
function merge(obj1, obj2) {
var result = {};
for (var prop in obj1) {
if (typeof obj1[prop] === "object" && typeof obj2[prop] === "object")
result[prop] = merge(obj1[prop], obj2[prop]);
else
result[prop] = obj1[prop];
}
for (var prop in obj2) {
result[prop] = (result[prop]? result[prop]: obj2[prop]);
}
return result;
}
console.log(merge(sportsA, sportsB));
This returns a new object, rather than modify an existing one, however.
In the first for..in loop, we check if we need to recurse first, otherwise set the property of result.
In the second for..in loop, we check if the property was already defined or if it's empty, and set the property accordingly.
Output:
{
"sports": {
"basketball": "kobe",
"football": "ronaldo",
"swimming": {},
"running": "",
"highJump": ""
},
"calendar": ["21", "25", "30"]
}
JSFiddle demo
The logic is breaking because when you only loop the property keys in one of the objects, you won't see the property keys that only exist in the other object.
You can get the root level keys of an object using Object.keys() which returns an array of the property names. Then you can merge the 2 sets of keys at same level and know all the final output properties needed
Then iterate those to get final results

JSON.stringify different from direct access to object property, strongloop

I use strongloop to build my api.
On a particular route the query includes model's relations. I get an array of objects that I would like to arrange.
In this particular arranging function I face the following problem.
The function receive an object named "item" containing a "trans" field (this field is an array of another object).
this piece of code :
console.log(JSON.stringify(item, null, 2));
produces this result :
{
"id": 1,
"created": "2015-08-19T21:04:16.000Z",
"updated": null,
"authorid": 0,
"likes": 0,
"shares": 0,
"fav": 0,
"validated": 0,
"comments": 0,
"trans": [
{
"text": "Première question en français",
"questionId": 1
}
],
"answers": [
{
"id": 1,
"questionid": 1,
"questionId": 1,
"trans": [
{
"text": "q1 : reponse 1 en francais",
"answerId": 1
}
]
},
{
"id": 2,
"questionid": 1,
"questionId": 1,
"trans": [
{
"text": "q1 : reponse 2 en francais",
"answerId": 2
}
]
}
]
}
This problem is when I try to reach this part :
item.trans[0].text
console says "item.trans is undifined" and when I try this piece of code :
console.log(item.trans);
I have this result :
function (condOrRefresh, options, cb) {
if (arguments.length === 0) {
if (typeof f.value === 'function') {
return f.value(self);
} else if (self.__cachedRelations) {
return self.__cachedRelations[name];
}
} else {
if (typeof condOrRefresh === 'function'
&& options === undefined && cb === undefined) {
// customer.orders(cb)
cb = condOrRefresh;
options = {};
condOrRefresh = undefined;
} else if (typeof options === 'function' && cb === undefined) {
// customer.orders(condOrRefresh, cb);
cb = options;
options = {};
}
options = options || {}
// Check if there is a through model
// see https://github.com/strongloop/loopback/issues/1076
if (f._scope.collect &&
condOrRefresh !== null && typeof condOrRefresh === 'object') {
//extract the paging filters to the through model
['limit','offset','skip','order'].forEach(function(pagerFilter){
if(typeof(condOrRefresh[pagerFilter]) !== 'undefined'){
f._scope[pagerFilter] = condOrRefresh[pagerFilter];
delete condOrRefresh[pagerFilter];
}
});
// Adjust the include so that the condition will be applied to
// the target model
f._scope.include = {
relation: f._scope.collect,
scope: condOrRefresh
};
condOrRefresh = {};
}
return definition.related(self, f._scope, condOrRefresh, options, cb);
}
}
How can I simply access the "trans" property in this case to get the text inside ?
(Not really at easy in js)
Thanks in advance.
It's possible that your item object has implemented the toJSON function.
Pop open your browser's console and run this snippet to see an example of how this can make a difference between the stringified JSON and the actual object:
var x = {
name: "foo",
children : function() {
return [ { name: 'child 1' }, { name: 'child 2' } ];
},
toJSON: function() {
var simplified = { name: this.name, children: this.children() };
return simplified
}
};
// shows children as a simple array
console.log( JSON.stringify( x, null, 2 ) );
// {
// "name": "foo",
// "children": [
// {
// "name": "child 1"
// },
// {
// "name": "child 2"
// }
// ]
// }
// oops... not what you expected
console.log( x.children[0].name );
// Uncaught TypeError: Cannot read property 'name' of undefined
Of course, the easiest fix would be to parse the stringify result:
var y = JSON.parse( JSON.stringify( x ) );
console.log( y.children[0].name );
It's a last-case-scenario-type solution, though, since JSON.stringify is a very expensive function.

Categories