Accessing items pushed into array - javascript

Within my firebase data I have a games object, and a players object. Nested within the players object are unique keys that match the games unique keys, these are the games that include players. Then nested within the game unique keys (inside the players object) are the keys of the actual players who entered that particular game. what I'm trying to accomplish is an ng-repeat of only the games that the current user (player.uid) has joined. So in the code below I'm checking if the players id (inside the players object) matches their firebase UID, and if it does I'm pushing that into empty array, then the loop should iterate over that array and if the keys in the array match the keys in the games object...it returns true; switching the class from 'hideGames', which has a display of none, to 'show games' which has a display of block. This works well if I add the keys to the commented out gamesToShow array, but not in the actual gamesToShow array that should be populated from the loop. What am I doing wrong here? I have tried moving the gamesToshow array but it still logs empty. What do I need to do in order for it to be usable in the for loop? Thanks in advance, I posted relevant code, if any thing else is needed let me know. Thanks everyone.
"games":{
"-JwYx6ckhITt2GWOmzLy":{
"date":"8/27/2015",
"host":"Anthony DeVenuto",
"hostEmail":"anthonydevenuto#gmail.com",
"location":{
"address":"1234 Crooked Rd, Chicago Illl",
"course":"Crooked Stick"
},
"name":"Crooked Stick Run",
"rules":{
"amount":"21",
"format":"Match Play",
"holes":"9",
"perBirdie":"DOES NOT APPLY",
"perSkin":"DOES NOT APPLY",
"time":"12:00pm"
}
},
"-Jwi64w0weox4vxIbz8J":{
"date":"8/23/2015",
"host":"Anthony DeVenuto",
"hostEmail":"anthonydevenuto#gmail.com",
"location":{
"address":"1234 fdsadgad",
"course":"West Hills Gathering"
},
"name":"West Side Shuffle",
"rules":{
"amount":"21",
"format":"Match Play",
"holes":"18",
"perBirdie":"DOES NOT APPLY",
"perSkin":"DOES NOT APPLY",
"time":"1:00pm"
}
},
"-Jwx-f7HnjIKdkMnM16D":{
"date":"8/23/2015",
"host":"Andy",
"hostEmail":"andy#andy.com",
"location":{
"address":"1234 First Ave",
"course":"WestCode Hills"
},
"name":"WestCode Hustle",
"rules":{
"amount":"12",
"format":"Match Play",
"holes":"18",
"perBirdie":"DOES NOT APPLY",
"perSkin":"DOES NOT APPLY",
"time":"1:00pm"
}
}
},
"players":{
"-JwYx6ckhITt2GWOmzLy":{
"-Jx1uw6iY87HoNJfAngF":{
"email":"andy#andy.com",
"id":"simplelogin:19",
"name":"Andy"
}
},
"-Jwi64w0weox4vxIbz8J":{
"-Jx1uxoJ0H8Pycp7V12s":{
"email":"andy#andy.com",
"id":"simplelogin:19",
"name":"Andy"
}
},
"-Jwx-f7HnjIKdkMnM16D":{
"-Jx1nbKxyLcbwFFIGjh4":{
"email":"anthonydevenuto#gmail.com",
"id":"simplelogin:22",
"name":"Anthony DeVenuto"
}
}
},
"users":{ }
}
JS
var player = auth.$getAuth();
$scope.displayGames = function(game){
var gamesToShow = [];
// var gamesToShow = ['-JwYx6ckhITt2GWOmzLy', '-Jwi64w0weox4vxIbz8J'];
var playersRef = fire.child('players');
playersRef.on('value', function(snapshot){
var gamesObjects = snapshot;
gamesObjects.forEach(function(snapshot){
var gameKeys = snapshot.key()
var playerKeys = snapshot;
playerKeys.forEach(function(snapshot){
if (snapshot.val().id == player.uid) {
gamesToShow.push(gameKeys);
console.log(gameKeys)
}
});
});
});
for (var i=0;i<gamesToShow.length;i++) {
var uniqueKeys = gamesToShow[i];
if (game.$id == uniqueKeys) {
return true;
}
};
}
HTML template:
<h1>Dashboard</h1>
<ul>
<li class="hideGames" ng-repeat="game in games" ng-class="{showGames:displayGames(game)}">
Course: {{ game.name }} <br> Date:{{ game.date }}<br>
Remove
</li>
</ul>

If I parse correctly, you are trying to get a list of the games that the current user is playing in. If so, this code will do the trick:
var uid = 'simplelogin:19';
var gamesToShow = [];
ref.child('players').on('value', function(gamesSnapshot) {
gamesSnapshot.forEach(function(gameSnapshot) {
var playerKeys = Object.keys(gameSnapshot.val());
playerKeys.forEach(function(playerKey) {
var player = gameSnapshot.val()[playerKey];
if (player.id === uid) {
console.log(gameSnapshot.key());
gamesToShow.push(gameSnapshot.val());
}
});
});
console.log(gamesToShow);
});
A JSBin that shows this code working: http://jsbin.com/selisa/edit?js,console
But not that your data structure is pretty bad for the goal you're trying to accomplish. You're looping through the players to match them by their key and that makes things complex. Since players have an existing natural key (their uid) and each player can probably join each game only once, you're better off storing the players in a game under their uid:
{
"-JwYx6ckhITt2GWOmzLy": {
"simplelogin:19": true
},
"-Jwi64w0weox4vxIbz8J": {
"simplelogin:19": true
},
"-Jwx-f7HnjIKdkMnM16D": {
"simplelogin:22": true
}
}
With this data structure, you can simply get the list of games with a Firebase query:
ref.child('players')
.orderByChild('simplelogin:19')
.equalTo(true)
.once('value', function(snapshot) {
console.log(snapshot.val());
});
As is quite often the case, you can prevent many headaches by optimizing your data structure for how you intend to use it.
Yet another alternative you may want to consider is to store the games for each player/user under their /users node. That way you wouldn't even need a query, but you could directly access the games with:
ref.child('users').child('simplelogin:19').child('games').once('value', ...
That's going to be the best-performing solution, since it doesn't even require a query to access the games.

Related

Extract specific nested array in JSON Objects that match data with Javascript

I'm working with an NBA API where one of the features is finding players by their last name.
The issue I have; is that multiple players can have the same last name, of course.
An example of the response from the API when sorting with last names:
"players": [
0: {
"firstName":"Anthony"
"lastName":"Davis"
"teamId":"17"
"yearsPro":"9"
"collegeName":"Kentucky"
"country":"USA"
"playerId":"126"
"dateOfBirth":"1993-03-11"
"affiliation":"Kentucky/USA"
"startNba":"2012"
"heightInMeters":"2.08"
"weightInKilograms":"114.8"
1: {
"firstName":"Deyonta"
"lastName":"Davis"
"teamId":"14"
"yearsPro":"3"
"collegeName":"Michigan State"
"country":"USA"
"playerId":"127"
"dateOfBirth":"1996-12-02"
"affiliation":"Michigan State/USA"
"startNba":"2016"
"heightInMeters":"2.11"
"weightInKilograms":"107.5"
}
I limited the results here, but it goes on and on, etc.
So, I am looking to do two things:
First, extract/filter the correct player using their first name and last name.
In said extraction, I still need the complete array information when it is matched.
So essentially, I want 'Deyonta Davis', but when found - I also need the rest of said player's information (years pro, college, country, etc.)
I already have a command set up to retrieve the first result of the nested data in this API via last name - where the command takes the last name you input and sends the first result. The precise problem is that the first result is likely not to be the guy you are looking for.
The goal is to include first & last name to avoid pulling the wrong player.
A snippet of how I currently call the information via last name:
// Calling API
const splitmsg = message.content.split(' ')
var lastnameurl = "https://api-nba-v1.p.rapidapi.com/players/lastName/" + splitmsg[1];
axios.get(lastnameurl, {
headers: {
"x-rapidapi-key": apikey,
"x-rapidapi-host": apihost
}
// Extracting Player Information (first result)
var playerfirstname = response.data.api.players[0].firstName;
var playerlastname = response.data.api.players[0].lastName;
var collegename = response.data.api.players[0].collegeName;
var countryname = response.data.api.players[0].country;
var playerDOB = response.data.api.players[0].dateOfBirth;
var yrspro = response.data.api.players[0].yearsPro;
var startednba = response.data.api.players[0].startNba;
Any help would be appreciated, thank you.
If I understand the question correctly the task is:
Retrieve first matching object from an array where properties firstName and lastName equal to desired values.
To achieve this you could use build in find function.
const player = array.find(el => {
return el.firstName === "Deyonta" && el.lastName === "Davis"
});
Keep in mind if there is no such object in array the player will be undefined.

How to count a huge list of items

I have a huge list of items about almost all the crops and these data is to be plotted using maps and charts. I would like to count the number of each crop, say how many times was cabbage planted. I use Firebase database to store the data and I retrieve it using this function below:
database = firebase.database()
var ref = database.ref('Planting-Calendar-Entries');
ref.on('value', gotData, errData);
function gotData(data){
console.log(data.val())
var veggie = data.val();
var keys = Object.keys(veggie);
console.log(keys);
let counter = 0
for (var i = 0; i < keys.length; i++){
var k = keys[i];
var Veg_planted = veggie[k].Veg_planted;
var coordinates = veggie[k].coordinates;
if (Veg_planted == 'Cabbage'){
counter++;
}
// vegAll = Veg_planted.count()
console.log(Veg_planted, coordinates)
}
console.log(counter)
}
function errData(err){
console.log('Error!');
console.log(err)
}
This data I retrieve it from the database where it gets updated whenever someone submits their planting information. The code I used above will only apply if my list is small, but I have a list of about 170 items and it would be hard to write code to count each crop individually using something like let counter = 0, counter++. Is there a way I could navigate around this?
I'm assuming data.val() returns an array, not an object, and you're misusing Object.keys() on an array instead of just looping over the array itself. If that's true, then it sounds like you want to group by the Veg_planted key and count the groupings:
const counts = Object.values(veggie).reduce((counts, { Veg_planted }) => ({
...counts,
[Veg_planted]: (counts[Veg_planted] || 0) + 1
}), {});
Usage:
const veggie = [{ Veg_planted: 'Cabbage' }, { Veg_planted: 'Cabbage' }, { Veg_planted: 'Corn' }];
// result of counts:
// {Cabbage: 2, Corn: 1}
Actually: the code to count the items is probably going to be the same, no matter how many items there are. The thing that is going to be a problem as you scale though is the amount of data that you have to retrieve that you're not displaying to the user.
Firebase does not support aggregation queries, and your approach only works for short lists of items. For a more scalable solution, you should store the actual count itself in the database too.
So:
Have a blaCount property for each bla that exists.
Increment/decrement the counter each time your write/remove a bla to/from the database.
Now you can read only the counters, instead of having to read the individual items.
Firestore would be better option. You can query based on the field value.
var plantingRef = db.collection("PlantingCalendarEntries");
var query = plantingRef.where("Veg_planted", "==", "Cabbage");
if you still want to stuck with realtime database.
Save Counters to database.
Or use cloud dunctions to count.

How to alter an item in an object using JavaScript to replace special characters that are not accepted by Firebase

I am trying to push some Kubernetes stats to my Firebase Real-time Database (not Firestore) and due to the special character "/" in the keys, Firebase is throwing an error. I tried to search for similar issues here but they are not quite similar to mine (tried so many replace or even delete functions but did not work). Here are my objects:
=====>> THE OBJECT
So i want to change the "/" to something like "-" on order to be able to push the entire object as to Firebase.
Example:
beta.kubernetes.io/arch --> beta.kubernetes.io-arch
beta.kubernetes.io/os --> beta.kubernetes.io-os
kubernetes.io/hostname --> kubernetes.io-hostname
node-role.kubernetes.io/master --> node-role.kubernetes.io-master
Please note that i have to process this in the attached object screenshot. So i have to process this: k8snodes{obj} -> items[arr] -> metadata{obj} -> labels{obj} in order to reach the keys i want to replace, and then push the entire object (k8snodes) to firebase.
I am using Javascript/NodeJS. thank you so much.
This is how you do it according to your data structure.
.reduce() reference: reduce
var k8snodes = {
items: [
{
"metadata": {
"labels": {
"beta.kubernetes.io/arch": "amd",
"beta.kubernetes.io/os": "linux"
}
}
},
{
"metadata": {
"labels": {
"kubernetes.io/hostname": "centos-master-node",
"node-role.kubernetes.io/master": "master"
}
}
}
]
}
k8snodes.items.forEach(function(data){
var newK8snodes = Object.keys(data.metadata.labels).reduce((total,currentValue) => {
var newLabelKey = currentValue.replace(/\//g,'-')
var newLabel = {[newLabelKey]: data.metadata.labels[currentValue]}
total = {...total, ...newLabel}
return total;
}, {});
data.metadata.labels = newK8snodes
});
console.log(k8snodes);
another simple solution is
var a = 'beta.kubernetes.io/arch';
console.log(a.replace('/','-'))
const ob = {
'beta.kubernetes.io/arch': 'amd',
'beta.kubernetes.io/os': 'linux',
'kubernetes.io/hostname': 'centos',
'node-role.kubernetes.io/master': 'master'
}
const newOb = {};
for(var i in ob) {
const newKey = i.replace(/\//g,'-')
newOb[newKey] = ob[i];
}
console.log(newOb);

Couchdb join two documents using key

I have two documents one with tree structure and the other one relation to the first doc. Im trying to join these two doc`s by fk and pk. I couldnt get the actual results and it displays all null values.
First doc
{
"name": "one",
"root": {
"level1" : {
"level2" : {
"level3" : {
"itemone": "Randomkey1",
"itemtwo": "Randomkey2
}
}
}
},
"type": "firstdoc"
}
Second doc
{
"name" : "two",
"mapBy" : "Randomkey1",
"type" : "senconddoc
}
I`ve written a map function, which lists all the keys given a level 1 or 2 or 3 . Now I want o join this first doc and second doc using the key. Ive tried two ways (first: Im getting all (Root, Randomkey), (docName, Randomkey1) but it doesnt do any join. Im looking for a result like
(Root, docName)
Could someone assist in fixing this
map
function(doc) {
if (doc.type === 'firstdoc' || doc.type === 'seconddoc' ) {
var rootObj = doc.Root;
for (var level1 in rootObj) {
var level2Obj = doc.Root[level1];
for (var level2 in level2Obj) {
var keys = new Array();
var level3Obj = level2Obj[level2];
for (var i in level3Obj) {
var itemObj = level3Obj[i];
for (var i in itemObj) {
keys.push(itemObj[i]);
emit(doc.name, [itemObj[i], 0]);
var firstDocName = doc.name;
//This is gives null values
if (doc.Type === 'senconddoc' && doc.mapBy === itemObj[i]) {
emit(firstDocName , doc);
}
}
}
}
}
}
//This just lists keys to me
if (doc.type === 'senconddoc') {
emit([doc.mapBy, 1] , doc);
}
}
To simulate joins you have to output a doc with an _id in it, the value of the _id needs to point to an actual _id of a document. Then you can make use of include_docs=true to pull in the related documents. Example with many-to-many here: http://danielwertheim.se/couchdb-many-to-many-relations/
If this is not applicable, you can make a two step manual join by first returning custom keys. Then make a second query against the all documents view, with multiple keys specified.
It is much late but For such kind of tree structure, documents should be kept separately such as
{
id="firstDoc",
type="rootLevel"
}
{
id="secondDoc",
type="firstLevel"
parent="firstDoc"
}
{
id="thirdDoc",
type="firstLevel",
parent="firstDoc"
}
Now different levels can be joined using the Map Reduce function, Make sure that you will use it in proper way, Also use Logging so that you will be able to know in which sequence map/reduce function are being called by CouchDB.
Further, map Function should only be used for emitting the required document suppose if you want to to emit your level3 then in emit's value part, root.level1.level2.level3 should be there.
For more detail about the join you can refer
CouchDB Join using Views (map/reduce)

Organize a backbone.js collection?

How does one create a collection (in backbone.js) that is organized by an id. For example I would think it's quite common that collections need to be organized by date, category, keyword, type the list goes on. In my case I am trying to find an elegant way to organize players to team.
I don't think I need a plugin for this, my goal is just to organize the players to be grouped inside a div while using one clean json file so I can have a nice organized list. Again ideally it would be nice to use one json file for this, and structure the HTML similar to how the json itself is structured in terms of nesting, in my example league being the parent of everything, then team being the parent of the players.
I have gone through many backbone tutorials multiple times, and understand the flow and syntax pretty comfortably, but all tutorials work with one collection outputting a simple list in no specific order.
Basically I want to be able to create a clean json array like below. Then turn it into nice organized HTML.
{
"league":
[{
"team":"Lakers",
"players": [
{
"name": "Kobe"
},
{
"name": "Steve"
}
]
},
{
"team":"Heat",
"players": [
{
"name": "LeBron"
},
{
"name": "Mario"
}
]
}]
}
So this json structure is valid but it's not one I have used it's nested a little bit more, so accessing the models requires a little different technique, I am hoping to learn if this kind of array is ideal? Also if so how would I group say Kobe, Steve inside a div with perhaps the class name Lakers whilst obviously separating it from LeBron and Mario keeping those two inside a div with again perhaps a class of Heat.
Another example I could use would be like a actors to movies collection, again is the json format ideal (seems like it to me) here I obviously group actors to their respected movie? I would greatly appreciate some tips on building a clean view or views with a template or templates for outputting this nice and tidy.
{
"movies":
[{
"title":"Prisoners",
"actors": [
{
"name": "Hugh Jackman"
},
{
"name": "Jake Gyllenhaal"
}
]
},
{
"title":"Elysium",
"actors": [
{
"name": "Matt Damon"
},
{
"name": "Jodie Foster"
}
]
}]
}
Again just working with this json data and backbone.js how do I create a maintainable view for this array?
Final Note: I was able to successfully group players to teams using two json files, and assigning a player and id that matched the team id, then looped the team collection with the player collection inside using where to organize it (I am trying to rethink this better). To me this is not taking advantage of backbone, it can get confusing and just seems wrong to me. So again I hope to improve my knowledge here and get better. I would immensely appreciate clear concise information, I really struggle to wrap my head around this topic :)
THANKS!!
Keep your JSON in a Backbone friendly structure, this will mean your models are easily organised once they are placed into the collection.
JSON example
[
{ league : 1, team : 'lakers', player : 'kobe' }
{ league : 1, team : 'lakers', player : 'steve' }
// And so on
]
Consider that most backbone collections are built via a RESTful JSON api this would be easy to fetch directly into a collection and then sorted. The comparator() function on the collection is run each time a model is added to the collection, or when you ask for it to run.
Backbone collection example
var Players = Backbone.Collection.extend({
initialize : function() {
// Grab the JSON from the API
// this.fetching is now a deferred object
this.fetching = this.fetch();
}
// Comparator example one, as a string
comparator : 'team',
// Comparator example two, as a function
comparator : function(model) {
return model.get('team');
}
});
The comparator as a function approach is obviously better suited to more complex sort algorithms, otherwise the comparator as a string approach would be better. Bear in mind that the function approach, though a string can be returned (such as above), -1 or 1 would be better return values to indicate it's sort position.
Comparator example with model comparison
comparator : function(modelA, modelB) {
var teamA = modelA.get('team'),
playerA = modelA.get('player'),
teamB = modelB.get('team'),
playerB = modelB.get('player');
if (teamA < teamB) { // string comparison
return 1;
} else if (playerA < playerB} {
return 1;
} else {
return -1;
}
}
Now whenever a model is added to the collection it is sorted into it's correct location, if using the last example, by team and then by player name.
Simple view example using the collection
var ViewExample = Backbone.View.extend({
el : "#example-container",
render : function() {
var self = this;
// Use the deffered object to make sure models are
// all available in the collection before we render
this.collection.fetching.done(function() {
self.collection.each(function(model) {
self.$el.append('<p>' + model.get('player') + '</p>');
});
});
return this;
}
});
// Create the view and pass in the collection
// that will immediately fetch it's models
var view = new ViewExample({
collection : new Players()
});
Code here is untested
Start by building a working model of your data. Your JSON suggests a hierarchy : a collection of teams that each have a collection of players. Here's a possible implementation :
var Player = Backbone.Model.extend();
var Players = Backbone.Collection.extend({
model: Player
});
var Team = Backbone.Model.extend({
constructor: function(data, opts) {
// I like my subcollections as attributes of the model
// and not on the settable properties
this.players = new Players();
Backbone.Model.call(this, data, _.extend(opts, {parse: true}));
},
parse: function(data) {
// Players are handled in a subcollection
if (_.isArray(data.players))
this.players.reset(data.players);
// They are removed from the model properties
return _.omit(data, 'players');
}
});
var Teams = Backbone.Collection.extend({
model: Team,
parse: function(resp) {
return resp.league;
}
});
Now you can create your root collection. Note that you could also fetch it instead of instantiating it with the data.
// teams list
var teams = new Teams(data, {parse: true});
// for example, to get all players in all teams
var allplayers = _.flatten(teams.map(function(team) {
return team.players.models;
}));
console.log(_.invoke(allplayers, 'get', 'name'));
And a demo : http://jsfiddle.net/8VpFs/
Once you have your structure in place, you can worry about rendering it. Let's imagine you have this (Underscore) template
<ul>
<% _(teams).each(function(team) { %>
<li><strong><%= team.team %></strong>
<ul>
<% _(team.players).each(function(player) { %>
<li><%= player.name %></li>
<% }); %>
</ul>
</li>
<% }); %>
</ul>
You can alter your Team model to output a serialized representation of you model:
var Team = Backbone.Model.extend({
// ... as before
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.players = this.players.toJSON();
return json;
}
});
and render your template
var tpl = _.template($('#tpl').html());
var html = tpl({
teams: teams.toJSON()
})
$('body').append(html);
This usually would go into a view.
http://jsfiddle.net/8VpFs/1/
With a fetch
var teams = new Teams();
teams.fetch().then(function() {
var tpl = _.template($('#tpl').html());
var html = tpl({
teams: teams.toJSON()
});
$('body').append(html);
});
http://jsfiddle.net/8VpFs/2/

Categories