I am pretty new to learning to code. So sorry if this is a stupid question.
I have a nested object database that I want to search for a character name and then return to me who's character it is. But, so far, I can only find solutions that search the top level objects or are for arrays and I am running out of ideas.
Is it possible to search in depth for a name like 'Farah' and then somehow get 'olis characters' back?
Thanks in advance for any advice you guys might have!
{
"olis characters": {
"0": {
"name": "Farah",
"class": "rogue",
"level": 74
},
"1": {
"name": "Grop",
"class": "paladin",
"level": 31
},
"2": {
"name": "Skolmr",
"class": "druid",
"level": 85,
}
},
"chris characters": {
"0": {
"name": "Trygve",
"class": "bard",
"level": 28
},
"1": {
"name": "Brusi",
"class": "rogue",
"level": 10
},
"2": {
"name": "Steini",
"class": "skald",
"level": 58
}
}
}
As it is, your data is a little odd. You have and object with numeric keys, which suggests it should be an array. Having said that you can still search through the Object.values to get the data you want.
let data = {"olis characters": {"0": {"name": "Farah","class": "rogue","level": 74},"1": {"name": "Grop","class": "paladin","level": 31},"2": {"name": "Skolmr","class": "druid","level": 85,}},"chris characters": {"0": {"name": "Trygve","class": "bard","level": 28},"1": {"name": "Brusi","class": "rogue","level": 10},"2": {"name": "Steini","class": "skald","level": 58}}}
function findChar(name, data) {
for (let charGroup of Object.values(data)) {
let found = Object.values(charGroup).find(char => char.name === name)
if (found) return found
}
}
console.log(findChar('Grop', data))
console.log(findChar('Brusi', data))
// will return undefined if the name is not there:
console.log(findChar('Mark', data))
If you changed the data model to a simple array like:
let data = {
"olis characters": [{
"name": "Farah",
"class": "rogue",
"level": 74
},
{
"name": "Grop",
"class": "paladin",
"level": 31
}
],
"chris characters": [{
"name": "Trygve",
"class": "bard",
"level": 28
},
// ...
]
}
...you could avoid one of those Object.values and use find directly.
function findChar(name, data){
for (let charGroup of Object.values(data)){
let found = charGroup.find(char => char.name === name)
if (found) return found
}
}
It gets harder to run a loop through an object like this, maybe an object of objects is not the right data structure, consider using an array of object instead.
But May be this code can help you
const data = {
"olis characters": {
"0": {
"name": "Farah",
"class": "rogue",
"level": 74
},
"1": {
"name": "Grop",
"class": "paladin",
"level": 31
},
"2": {
"name": "Skolmr",
"class": "druid",
"level": 85,
}
},
"chris characters": {
"0": {
"name": "Trygve",
"class": "bard",
"level": 28
},
"1": {
"name": "Brusi",
"class": "rogue",
"level": 10
},
"2": {
"name": "Steini",
"class": "skald",
"level": 58
}
}
}
Object.keys(data).forEach(item => {
if(findItemByName('Farah', data[item])){
console.log(item)
}
})
function findItemByName(name, item) {
let r = false;
Object.keys(item).forEach(obj => {
if (item[obj].name == name) {
r = true;
}
});
return r;
}
Related
I need to check if a property in a complex object (nested objects with arrays) exists or not.
I found several posts on this subject, the most visited the one below.
The problem with the provided solution (checkNested function) doesn't work with objects with arrays.
Does anyone have a solution that cover this case as well?
Cheers.
javascript test for existence of nested object key
This the function I tested:
function checkProperty(obj, prop) {
var parts = prop.split('.');
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
if (obj !== null && typeof obj === "object" && part in obj) {
obj = obj[part];
} else {
return false;
}
}
return true;
}
This is an example of my object:
{
"_msgid": "3ae30deb.af9962",
"topic": "",
"payload": "I am really upset terrible service",
"error": null,
"parts": {
"id": "3ae30deb.af9962",
"type": "array",
"count": 2,
"len": 1,
"index": 0
},
"case_id": "0001",
"features": {
"usage": {
"text_units": 1,
"text_characters": 34,
"features": 7
},
"sentiment": {
"document": {
"score": -0.912124,
"label": "negative"
}
},
"semantic_roles": [{
"subject": {
"text": "I"
},
"sentence": "I am really upset terrible service",
"object": {
"text": "really upset terrible service",
"keywords": [{
"text": "terrible service"
}]
},
"action": {
"verb": {
"text": "be",
"tense": "present"
},
"text": "am",
"normalized": "be"
}
}],
"language": "en",
"keywords": [{
"text": "terrible service",
"sentiment": {
"score": -0.912124
},
"relevance": 0.902721,
"emotion": {
"sadness": 0.462285,
"joy": 0.002207,
"fear": 0.125395,
"disgust": 0.17766,
"anger": 0.575927
}
}],
"entities": [],
"emotion": {
"document": {
"emotion": {
"sadness": 0.462285,
"joy": 0.002207,
"fear": 0.125395,
"disgust": 0.17766,
"anger": 0.575927
}
}
},
"concepts": [],
"categories": [{
"score": 0.99946,
"label": "/health and fitness/disease/headaches and migraines"
}, {
"score": 0.0155692,
"label": "/education/school"
}, {
"score": 0.0141217,
"label": "/family and parenting/children"
}]
}
}
And a failure test:
console.log(checkProperty(msg, 'features.keywords[0].text') ? msg.features.keywords[0].text : "NA");
The checkProperty function you're using doesn't recognize brackets ([ and ]), it only understands dots. So, just give it dots:
checkProperty(msg, 'features.keywords.0.text');
I have some data and I need a loop which creates 2 arrays...
So I first create the 2 arrays:
namelist = [];
countList = [];
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
}
The desired result for this example would be:
For namelist:
['name 1', 'name 2']
For countList:
[5, 10]
How can I do this?
var nameList = [];
var countList = [];
var myObj =
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
for(var key in myObj.sub){
nameList.push(myObj.sub[key].name);
countList.push(myObj.sub[key].stats.count);
}
console.log(nameList);
console.log(countList);
for(var key in obj.sub){
nameList.push(obj.sub[key].name);
countList.push(obj.sub[key].stats.count;
}
Object.keys may help you to walk through object properties. Example related to your object:
var namelist = [],
countList = [],
obj = {
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
Object.keys(obj.sub).forEach(function(item) {
namelist.push(obj.sub[item].name);
countList.push(obj.sub[item].stats.count);
});
console.log(namelist, countList);
Working example: https://jsfiddle.net/ry0zqweL/
Obviously, you can optimise it in many ways. It's just illustrating one of the many solutions.
I want to check if a value doesn't exist in the given object, by filtering an array of string.
I want to check if the values in the keys array are contained in the JSON object I'm looping. If one of the values isn't, I have to do something else, but only if the non-existent value (in resArray) is contained in the keys array.
JSON here
Here's what I tried:
var keys = [
"total_kills",
"total_deaths",
"total_planted_bombs",
"total_defused_bombs",
"total_kills_knife",
"total_kills_headshot",
"total_wins_pistolround",
"total_wins_map_de_dust2",
"last_match_wins",
"total_shots_fired",
"total_shots_hit",
"total_rounds_played",
"total_kills_taser",
"last_match_kills",
"last_match_deaths",
"total_kills_hegrenade",
];
var resArray = stats.playerstats.stats;
var statsArray = [];
for (var i = 0; i < keys.length; i++) {
for(var j = 0; j < resArray.length; j++){
//if the value in keys array exists, do something
if(resArray[j]["name"] === keys[i]){
//do something
}
if(<value doesn't exist)>)
//do something else.
}
}
Solved:
function contains(obj, key, value) {
return obj.hasOwnProperty(key) && obj[key] === value;
}
var resArray = stats.playerstats.stats;
var statsArray = [];
for (var i = 0; i < keys.length; i++) {
resArray.some(function(found){
if(contains(found, "name", keys[i])){
statsArray.push(found);
}
});
if(typeof statsArray[i] == 'undefined'){
console.log("Not present in array: " + keys[i]);
statsArray.push({"name": keys[i], "value": 'None'});
}
}
Thanks to everyone has replied to this thread.
Your example insinuates that you're creating a new array based off the stats and conditional presence of your provided keys. An easy way to build this array would be to use Array.prototype.map to enumerate over your stats array. Next, in each iteration's callback you can pass the name property as an argument to keys.indexOf to check if that particular name is present in your keys array.
var statsArray = stats.map(function(stat) {
if (keys.indexOf(stat.name) > -1) {
return stat;
} else {
return stat.name + ' not found.';
}
});
This will yield a new array which will contain either the stat object or a not regarding its absence in keys. However, you can return whatever your heart desires, as long as it's a valid array item.
Here's a working example with a small chunk of your dataset (but will work with your original dataset):
var keys = [
"total_kills",
"total_deaths",
"total_planted_bombs",
"total_defused_bombs",
"total_kills_knife",
"total_kills_headshot",
"total_wins_pistolround",
"total_wins_map_de_dust2",
"last_match_wins",
"total_shots_fired",
"total_shots_hit",
"total_rounds_played",
"total_kills_taser",
"last_match_kills",
"last_match_deaths",
"total_kills_hegrenade",
];
var stats = [{
"name": "total_kills",
"value": 25305
}, {
"name": "total_deaths",
"value": 27474
}, {
"name": "total_time_played",
"value": 1822419
}, {
"name": "total_planted_bombs",
"value": 1397
}, {
"name": "total_defused_bombs",
"value": 239
}, {
"name": "total_wins",
"value": 11477
}, {
"name": "total_damage_done",
"value": 3783962
}, {
"name": "total_money_earned",
"value": 65159500
}, {
"name": "total_rescued_hostages",
"value": 1
}, {
"name": "total_kills_knife",
"value": 278
}, {
"name": "total_kills_hegrenade",
"value": 168
}, {
"name": "total_kills_glock",
"value": 699
}, {
"name": "total_kills_deagle",
"value": 1289
}, {
"name": "total_kills_elite",
"value": 37
}, {
"name": "total_kills_fiveseven",
"value": 165
}, {
"name": "total_kills_xm1014",
"value": 78
}, {
"name": "total_kills_mac10",
"value": 154
}, {
"name": "total_kills_ump45",
"value": 330
}, {
"name": "total_kills_p90",
"value": 1105
}, {
"name": "total_kills_awp",
"value": 6934
}, {
"name": "total_kills_ak47",
"value": 4528
}, {
"name": "total_kills_aug",
"value": 137
}, {
"name": "total_kills_famas",
"value": 540
}, {
"name": "total_kills_g3sg1",
"value": 116
}, {
"name": "total_kills_m249",
"value": 50
}, {
"name": "total_kills_headshot",
"value": 7112
}, {
"name": "total_kills_enemy_weapon",
"value": 2308
}, {
"name": "total_wins_pistolround",
"value": 843
}, {
"name": "total_wins_map_cs_assault",
"value": 9
}, {
"name": "total_wins_map_cs_italy",
"value": 15
}, {
"name": "total_wins_map_cs_office",
"value": 11
}, {
"name": "total_wins_map_de_aztec",
"value": 71
}, {
"name": "total_wins_map_de_cbble",
"value": 373
}, {
"name": "total_wins_map_de_dust2",
"value": 4857
}, {
"name": "total_wins_map_de_dust",
"value": 25
}, {
"name": "total_wins_map_de_inferno",
"value": 777
}, {
"name": "total_wins_map_de_nuke",
"value": 247
}, {
"name": "total_wins_map_de_train",
"value": 47
}, {
"name": "total_weapons_donated",
"value": 2466
}, {
"name": "total_broken_windows",
"value": 30
}, {
"name": "total_kills_enemy_blinded",
"value": 566
}, {
"name": "total_kills_knife_fight",
"value": 67
}, {
"name": "total_kills_against_zoomed_sniper",
"value": 2284
}, {
"name": "total_dominations",
"value": 270
}, {
"name": "total_domination_overkills",
"value": 225
}, {
"name": "total_revenges",
"value": 207
}, {
"name": "total_shots_hit",
"value": 83704
}, {
"name": "total_shots_fired",
"value": 399207
}, {
"name": "total_rounds_played",
"value": 23419
}, {
"name": "total_shots_deagle",
"value": 12137
}, {
"name": "total_shots_glock",
"value": 21299
}, {
"name": "total_shots_elite",
"value": 777
}, {
"name": "total_shots_fiveseven",
"value": 3385
}, {
"name": "total_shots_awp",
"value": 22667
}];
var statsArray = stats.map(function(stat) {
if(keys.indexOf(stat.name) > -1) {
return stat;
} else {
return stat.name + ' not present in keys';
}
});
console.log(statsArray);
You can achieve what you want by using a combination of array functions. For example:
let stats = data.playerstats.stats;
let matches = stats.filter(i => keys.indexOf(i.name) >= 0);
let matchKeys = matches.map(k => k.name);
let negatives = keys.filter(i => matchKeys.indexOf(i) < 0);
Then you can just loop through the matches/negatives to do what you want with them.
Fiddle here.
Am having an arrray of Objects which again contains the id's of the parent Object:
eg:-
$scope.result=[
{
"id": 1,
"name": ABC,
"type": PQR,
"parentId": 2
},
{
"id": 2,
"name": ABC,
"type": PQR,
"parentId": 1
}]
I would like to convert this JSON in to the following wherin I will be having the parentObject in place of parentId's
$scope.result=[
{
"id": 1,
"name": ABC,
"type": a,
"parentId": {
"id": 2,
"name": PQR,
"type": b,
"parentId": 1
}
},
{
"id": 2,
"name": PQR,
"type": b,
"parentId": {
"id": 1,
"name": ABC,
"type": a
}
}
]
Can anybody help me with this conversion..
Assuming parentId will reference to one element
angular.forEach($scope.result, function(result) {
var obj = $scope.result.filter(function(ele) {
return result.parentId === ele.id;
})[0];
result.parentId = obj;
});
Since your tags doesn't contain angularjs, I'm using Array.prototype.forEach here
$scope.result.forEach(function(result) {
var obj = $scope.result.filter(function(ele) {
return result.parentId === ele.id;
})[0];
result.parentId = obj;
});
Will do 2 loops first one to fill a hash with keys of the ids, second will loop to replace parent id with actual object:
$scope = {}
$scope.result=[
{
"id": 1,
"name": "ABC",
"type": "PQR",
"parentId": 2
},
{
"id": 2,
"name": "ABC",
"type": "PQR",
"parentId": 1
}]
parents = {}
for(i=0;i<$scope.result.length;i++){
parents[$scope.result[i]["id"]] = jQuery.extend({}, $scope.result[i]); // Clone object
}
for(i=0;i<$scope.result.length;i++){
$scope.result[i]["parentId"] = parents[$scope.result[i]["parentId"]]
}
$scope.result
Fiddle Example
I want to convert this JSON data
var data = [
{
"computer": 24,
"brand": "Italy A",
"phone": 0,
"country": "Italy"
},
{
"brand": "Italy C",
"computer": 0,
"phone": 0,
"country": "Italy"
},
{
"brand": "Brazil B",
"computer": 0,
"phone": 22,
"country": "Brazil"
},
{
"computer": 0,
"brand": "Brazil D",
"phone": 62,
"country": "Brazil"
},
{
"computer": 34,
"brand": "US E",
"phone": 41,
"country": "US"
}
];
into a hierarchical form for a d3 graph:
{
"name": "categories",
"children": [
{
"name": "phone",
"children": [
{
"name": "US",
"children": [
{
"brand": "US E",
"size": 41
}
]
},
{
"name": "Brazil",
"children": [
{
"brand": "Brazil B",
"size": 22
},
{
"brand": "Brazil D",
"size": 62
}
]
},
{
"name": "Italy",
"children": []
}
]
},
{
"name": "computer",
"children": [
{
"name": "US",
"children": [
{
"brand": "US E",
"size": 34
}
]
},
{
"name": "Brazil",
"children": []
},
{
"name": "Italy",
"children": [
{
"brand": "Italy A",
"size": 24
}
]
}
]
}
]
}
I came up with this code to generate the format:
function group_children(data){
var categories = ["phone","computer"];
var countries = ["US","Brazil","Italy"];
var object = {name:"categories",children:[]};
for(var c =0; c < categories.length;c++){
object.children.push({"name":categories[c],children:[]});
for(var con = 0;con < countries.length;con++){
object.children[c].children.push({"name":countries[con],"children":[]});
}
}
for(var i = 0;i < data.length;i++){
var row = data[i];
for(var c =0; c < categories.length;c++){
for(var con = 0;con < countries.length;con++){
var cat_key = categories[c],
country_key = countries[con];
if(row[cat_key] > 0){
if(object.children[c].name == cat_key && row.country == country_key){ object.children[c].children[con].children.push({brand:row["brand"],size:row[cat_key]});
}
}
}
}
}
return object;
}
Is it possible , during the iteration, not to push a country into the brand or computer's children array if the country's children array is empty?
For example, these objects should be removed
// computer
{
"name": "Brazil",
"children": []
}
// phone:
{
"name": "Italy",
"children": []
}
Here's the part that push each country into each category's children array:
for(var c =0; c < categories.length;c++){
object.children.push({"name":categories[c],children:[]});
for(var con = 0;con < countries.length;con++){
object.children[c].children.push({"name":countries[con],"children":[]});
}
}
My approach is probably wrong, so any other suggestions converting the data into that hierarchical form is also appreciated.
Check this fiddle, is this what you're looking for? I decided to go for a different approach to the one you followed, hope you don't mind. I've commented the code so that it's clearer:
var result = {
name: "categories",
children: [{
"name": "phone",
"children": []
}, {
"name": "computer",
"children": []
}]
};
$.each(data, function (index, item) {// Go through data and populate the result object.
if (+item.computer > 0) { // Computer has items.
filterAndAdd(item, result.children[1], "computer");
}
if (+item.phone > 0) { // Phone has items.
filterAndAdd(item, result.children[0], "phone");
}
});
function filterAndAdd(item, result_place, type) {// Search and populate.
var i = -1;
$.each(result_place.children, function (index,a) {
if( a.name === item.country ) {
i = index;
return false;
}
});
if (i > -1) {// Country already exists, add to children array.
result_place.children[i].children.push({
"brand": item.brand,
"size": item[type]
});
} else {// Country doesn't exist, create it.
result_place.children.push({
"name": item.country,
"children": [{
"brand": item.brand,
"size": item[type]
}]
});
}
}
Hope it helps.
You have to use d3.nest() function to group array elements hierarchically. The documentation is available
here. Also go through this tutorial which could definitely help you to create hierarchical data.
That's not enough, you get hierarchical data in terms of key and value pairs. But if you want to convert into name and children, already a question on SO is asked, check this.
With your current approach you should iterate the data to find the empty ones, before pushing the countries, which would result in way more iteration than just simply iterating the result at the end to filter the empty ones.
Otherwise you should create the scruture in the same iterations of the data insertion, thus reducing the iterations to 1.