Javascript array.find() only finds within first instance - javascript

I have the following object array:
var data = {};
data.type = {
"types": [{
"testA": {
"testVar": "abc",
"testContent": "contentA"
}
}, {
"testB": {
"testVar": "def",
"testContent": "contentB"
}
}]
};
What I'm trying to do is find the value of testContent based on finding the object it belongs by searching it's parent and sibling:
/* within the data, find content where parent is testA and sibling testVar is "abc" */
var findSet = data.type.types.find(function(entry) {
return entry['testA'].testVar === "abc";
});
console.log(findSet['testA'].testContent); /* returns string "contentA" as expected */
This works fine for first object but fails to find next object, giving error:
Cannot read property 'testVar' of undefined
var findSet = data.type.types.find(function(entry) {
return entry['testB'].testVar === "def"; /* Cannot read property 'testVar' of undefined */
});
console.log(findSet['testB'].testContent);
How else could I find what's needed?
Here's a fiddle to test the output

var data = {};
data.type = {
"types": [{
"testA": {
"testVar": "abc",
"testContent": "contentA"
}
}, {
"testB": {
"testVar": "def",
"testContent": "contentB"
}
}]
};
var findSet = data.type.types.find(function(entry) {
return entry['testA'] && entry['testA'].testVar === "abc";
});
console.log(findSet['testA'].testContent);
var findSet = data.type.types.find(function(entry) {
return entry['testB'] && entry['testB'].testVar === "def"; /* Cannot read property 'testVar' of undefined */
});
console.log(findSet['testB'].testContent);
just check if your entry exist before testing his attribute.

Related

Object variable property undefined even if it's not

I'm trying to read the property of a json object using variables. If I use variables i get error, while if I use properties it works.
JSON:
{
"homebrews": {
"books": {
"title": "text."
},
"cards": {
"template": {
"id": 0,
"name": "myName"
}
}
}
}
Function called
createHomebrew('card');
function:
function createHomebrew(type) {
var homebrew;
$.getJSON('/data-files/templateHomebrew.json', function(json) {
var id = type + 's'; // cards
homebrew = json.homebrews[id].template // json.homebrews[id] is undefined
});
Instead
console.log(json.homebrews.cards.template); // Object { id: 0, name: "myName"}
Solved, since setting id = "cards" worked, for some reason the function called with createHomebrew('card') didn't recognize card as a String, even though console.log(typeof id) returned String. So I added id = id.toString();
function createHomebrew(type) {
var homebrew;
$.getJSON('/data-files/templateHomebrew.json', function(json) {
var id = type + 's';
id = id.toString();
homebrew = json.homebrews[id].template
});

Iterate through Nested Object's properties to flatten in an array using ES5 or lodash

I have a keys property that is related to map property. The length of keys correspond with how deep the level of each map property goes. In this case only 2 levels.
If I add another entry to keys then each map property will go one more level deeper.
Below is the data
{
keys: [
"vendorApNbr",
"type"
],
map: {
_default: { <-** 1st level
_default: "'100026'", <-** 2nd level
PT_CC: "'120035'", <-** 2nd level
PT_DC: "'120037'"
},
A-00: { <- ** 1st level
_default: "'120037'" <- ** 2nd level
},
A-01: {
_default: "'120035'"
},
A-02: {
_default: "'120035'"
},
A-03: {
_default: "'120036'"
},
A-04: {
_default: "'100024'"
}
}
}
I would like to create an array of arrays where each item in the array is iteration of going from level 1 to level 2 (but can go down more levels if needed)
i.e.
[
['_default', '_default', "'10026'"],
['_default', 'PT_CC', "'120035'"],
['_default', 'PP_DC', "'120037'"],
['A-00', '_default', "'120037'"],
['A-01', '_default', "'120035'"],
...etc
['A-04', '_default', "'100024'"]
]
I'm limited to ES5 or lodash. I'm thinking of recursion but not sure how to approach this. Any suggestion can help.
Edit
also to have a way to turn the array form back to nested object form
What about this? It doesn't care about how many nested the object is and what the level is. Additionally, each depth could be different.
var obj = {
"_default": {
"_default": "'100026'",
"PT_CC": "'120035'",
"PT_DC": "'120037'"
},
"A-00": {
"_default": "'120037'"
},
"A-01": {
"_default": "'120035'"
},
"A-02": {
"_default": "'120035'"
},
"A-03": {
"_default": "'120036'"
},
"A-04": {
"_default": "'100024'"
}
}
var result = [];
function rec(acc, obj) {
if (typeof obj === "object") {
for (var key in obj) {
rec(acc.concat([key]), obj[key]);
}
return;
}
result.push(acc.concat([obj]));
}
rec([], obj);
console.log(result);
You can do it by using Depth-Fist Search.
The code below is an example extracted from this webpage. The difference here is that is concatenates every key, but you can use the same algorithm with some modifications to get a list.
var obj = {
baz: {
foo: {
bar: "5"
},
hell: {
sin: "0"
}
},
a: {
b: "1"
}
};
var hash = {};
var str = '';
var dfs = function(obj, str) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'string')
hash[str + key] = obj[key];
else {
dfs(obj[key], str + key + '.');
}
}
}
};
dfs(obj, str);
console.log(hash);
You can use the following recursive function to flatten the object's properties in an array.
This function...
only takes one parameter as argument and
doesn't rely on external vars
var data = {
keys: [
"vendorApNbr",
"type"
],
map: {
_default: {
_default: "'100026'",
PT_CC: "'120035'",
PT_DC: "'120037'"
},
A00: {
_default: "'120037'"
},
A01: {
_default: "'120035'"
},
A02: {
_default: "'120035'"
},
A03: {
_default: "'120036'"
},
A04: {
_default: "'100024'"
}
}
};
function makeItFlat(data) {
var newArray = [];
var properties = Object.getOwnPropertyNames(data);
for (var prop of properties) {
if (typeof data[prop] === 'object') {
var flat = makeItFlat(data[prop]);
for (var f of flat) {
if (!Array.isArray(f)) {
f = [f];
}
newArray.push([prop].concat(f));
}
} else {
newArray.push([prop].concat([data[prop]]));
}
}
return newArray;
}
var flatArray = makeItFlat(data.map);
console.log(flatArray);
For converting the array back to the original object, you can use this code:
var flatArray = [
["_default", "_default", "'100026'"],
["_default", "PT_CC", "'120035'"],
["_default", "PT_DC", "'120037'"],
["A00", "_default", "'120037'"],
["A01", "_default", "'120035'"],
["A02", "_default", "'120035'"],
["A03", "_default", "'120036'"],
["A04", "_default", "'100024'"]
];
function convertArrayOfStringsToObject(flatArray) {
var newObject = {};
var key = flatArray[0];
var entry = null;
if (flatArray.length == 2) {
entry = flatArray[1];
} else {
entry = convertArrayOfStringsToObject(flatArray.slice(1));
}
if (key in newObject) {
//key exists already, then merge:
Object.assign(newObject[key], entry);
} else {
newObject[key] = entry;
}
return newObject;
}
function expandArray(flatArray) {
var newObject = {}
for (var line of flatArray) {
var key = line[0];
var entry = convertArrayOfStringsToObject(line.slice(1));
if (key in newObject) {
//key exists already, then merge:
Object.assign(newObject[key], entry);
} else {
newObject[key] = entry;
}
}
return newObject;
}
console.log(expandArray(flatArray));

'filter' filter in AngularJS

I have an array of objects and I want to extract the value when key is passes in 'filter' filter. Below is the controller code snippet I have tried, but the type of response I get is undefined. Please help me in finding where am I going wrong.
var states = [{"HIMACHAL PRADESH":"HP"},{"JAMMU AND KASHMIR":"JK"},{"JHARKHAND":"JH"},{"KARNATAKA":"KA"},{"KERALA":"KL"},{"MADHYA PRADESH":"MP"},{"MAHARASHTRA":"MH"},{"ORISSA":"OR"}]
var str = "ORISSA";
var abbr = $filter('filter')(states, {key: str}, true).value;
console.log ("ABBR:"+abbr);
P.S. I have injected $filter in the controller
Use Object.keys and find
var matchedState = states.find( s => Object.keys( s )[0] == str );
var abbr = matchedState ? matchedState[str] : ""
Demo
var states = [{
"HIMACHAL PRADESH": "HP"
}, {
"JAMMU AND KASHMIR": "JK"
}, {
"JHARKHAND": "JH"
}, {
"KARNATAKA": "KA"
}, {
"KERALA": "KL"
}, {
"MADHYA PRADESH": "MP"
}, {
"MAHARASHTRA": "MH"
}, {
"ORISSA": "OR"
}]
var str = "ORISSA";
var matchedState = states.find(s => Object.keys(s)[0] == str);
var abbr = matchedState ? matchedState[str] : ""
console.log(abbr);

how to find all leaves of group with javascript and json

I want to list all leaves ids where group name is i.e. "group110"
So, output for this example is 014, 288, 223 and 244.
Here is content of my JSON file:
{
"name": "visualization",
"children": [
{
"name": "group100",
"children": [
{
"name": "group110",
"children": [
{
"name": "group111",
"children": [
{"id":"014","link":"-","name":" Animals/70","decade":"-"}
]
},
{
"name": "group112",
"children": [
{"id":"288","link":"-","name":"Heidelberg platen press","decade":"1960s"}
]
},
{
"name": "group113",
"children": [
{"id":"223","link":"-","name":"Camera Praktica Super TL – shutter release","decade":"1960s"},
{"id":"244","link":"-","name":"Mechanical calculator, Facit","decade":"1950s"}
]
}
]
},
Try this way. Find the group using a recursive method and collect leaf nodes using another recursive method.
function getLeafNodes(leafNodes, obj){
if(obj.children){
obj.children.forEach(function(child){getLeafNodes(leafNodes,child)});
} else{
leafNodes.push(obj);
}
}
function findIds(json,name){
if(json.children){
if(json.name==name) {
var leafNodes = [];
getLeafNodes(leafNodes,json);
console.log(leafNodes.map(function(leafNode){ return leafNode.id; })); //Logs leaf node ids to the console
} else {
json.children.forEach(function(child){
findIds(child,name);
});
}
}
}
Execution of following code will print ["014", "288", "223", "244"]
findIds(actualJSON,"group110");
The following code traverses the tree recursively. If param has children, then its children will be traversed. Otherwise, its id will be appended to the results array, thus, at the end results will contain the id of the leaves. getResults returns results to simplify its usage.
var results = [];
function getResults(param) {
if (!!param.children) {
for (var child in param.children) {
getResults(param.children[child]);
}
} else {
results[results.length] = param.id;
}
return results;
}
Here is a generic terse recursive answer for finding nodes using some jquery ($.map).
Watch out for stack overflows if the data is deep though!
Also it will not continue searching inside a matching node for more matching sub nodes, so it's only applicable if the search term does not nest logically.
This method makes use of the array flattening feature of $.map.
var found = (function walk(obj, searchKey, searchTerm) {
if(!$.isPlainObject(obj)) return null;
return obj[searchKey] === searchTerm ? [obj] : $.map(obj, function (lev) {
return walk(lev, searchKey, searchTerm);
});
})(data, 'name', 'group110');
Expanding on that to solve the specific problem above...
var found = (function walk(obj, searchTerm) {
if(!$.isPlainObject(obj)) return null;
return obj.name == searchTerm
? $.map(obj.children, function(c){
return $.map(c.children, function(f){ return f.id; }); })
: $.map(obj.children, function (lev) {
return walk(lev, searchTerm); });
})(data, 'group110');
Or rather
var found = (function walk(obj, lambda, term) {
if(!($.isPlainObject(obj) || $.isArray(obj))) return null;
return lambda.call(obj, term)
? $.map(obj.children, function(c){
return $.map(c.children, function(f){ return f.id; }); })
: $.map(obj.children, function (lev) {
return walk(lev, searchTerm); });
})(data, function(a){ return this.name == a; }, 'group110');

Split array into new array based on property occurences

I have an array of objects that I would like to turn into an array (or array-like) object where the keys are the unique values for that given property (something like SQL group by).
fiddle:
var obj = [
{
"ClaimId":"111",
"DrugName":"AMBIEN CR",
"PatientId":1571457415
},
{
"ClaimId":"222",
"DrugName":"AMBIEN CR",
"PatientId":1571457415
},
{
"ClaimId":"333",
"DrugName":"LOTREL",
"PatientId":1571457415
},
{
"ClaimId":"444",
"DrugName":"METHYLPREDNISOLONE",
"PatientId":1571457415
},
{
"ClaimId":"555",
"DrugName":"CYMBALTA",
"PatientId":1513895252
},
{
"ClaimId":"666",
"DrugName":"CYMBALTA",
"PatientId":1513895252
},
{
"ClaimId":"777",
"DrugName":"CYMBALTA",
"PatientId":1513895252
},
{
"ClaimId":"888",
"DrugName":"CYMBALTA",
"PatientId":1513895252
},
{
"ClaimId":"147503879TMQ",
"DrugName":"CYMBALTA",
"PatientId":1513895252
},
{
"ClaimId":"999",
"DrugName":"CYMBALTA",
"PatientId":1513895252
}
]
function splitBy(data, prop) {
var returnObj = {};
var returnArray = [];
$.each(data, function (ix, val) {
if (returnObj[val[prop]] === undefined) {
returnObj[val[prop]] = [];
returnObj[val[prop]].push(val);
}
});
console.log(returnObj);
}
splitBy(obj,'PatientId');
In the fiddle you can see that I get the keys of the array like I want (the two unique values in the PatientId property) but I only get the first value. I understand that's because once the key is no longer undefined, then this check isn't ran, but I couldn't figure out quite how to do it and this is as close as I got. How can I do this with one iteration over this collection?
I only get the first value.
That's because you only push the first value - when there had been no key. Change the
if (returnObj[val[prop]] === undefined) {
returnObj[val[prop]] = [];
returnObj[val[prop]].push(val);
}
to
if (returnObj[val[prop]] === undefined) {
returnObj[val[prop]] = [];
}
returnObj[val[prop]].push(val);
jsFiddle Demo
You almost had it, but you forgot the else clause for once the key existed
if (returnObj[val[prop]] === undefined) {
returnObj[val[prop]] = [];
returnObj[val[prop]].push(val);
}else{
returnObj[val[prop]].push(val);
}

Categories