I want to order the query based on multiple values. The problem is, that I can't select the objects key type because I get undefined when I do so.
var filterDataAccordingToDate = function(ref, startTime, endTime, travelType) {
ref.orderByChild('date')
.startAt(startTime).endAt(endTime)
.once('value', function(snapshot) {
var travel = snapshot.val();
console.log("TRAVEL OBJ: " + util.inspect(travel, false, null));
console.log("TRAVEL TYPE: " + travel.type);
if (travel.type == travelType) {
// DO STUFF
}
});
}
The first console.log() returns the correct object:
TRAVEL OBJ: {
"-KKiZKAVH0-QulKnThhF" : {
"date" : 1466439009,
"dest" : 1,
"fbKey" : "-KKiZKAVH0-QulKnThhF",
"type" : 1
}
}
The second one: TRAVEL TYPE: undefined
Any idea, where I made a mistake?
Use the .forEach() method on DataSnapshot
snapshot.forEach(function(snap) {
var key = snap.key;
if (key === travelType) {
// Do stuff
}
});
Since you will be retrieving multiple objects you need to iterate over them to get the values for each one.
for (var key in travel) {
console.log("TRAVEL OBJ: " + util.inspect(travel[key], false, null));
console.log("TRAVEL TYPE: " + travel[key].type);
}
Related
I am new in Vuejs and I get an unexpected output in my app. What my app does is to search on YouTube API for channels, and then adding those channels in a list.
Then I like to render the list of the subscribed channels, but always I get one item less. Alway the last inserted item is missing from the rendered list, while the item exists in my data.
This is the rendered output:
If you see the right column, under the text box, has only one item rendered, while in my Vue console I have two items under the channels_info key:
Then if I try to append yet another one item in the list, the console will display 3 items while the HTML render will display 2, and so on.
My code is the following:
var setup = function () {
app = new Vue(
{
el : '#sml_app',
data : {
channel_name : '',
errors : [],
channels_found : {},
no_channels_found : true,
next_page_token : '',
prev_page_token : '',
page_token : '',
subscriptions : [],
channels_info : {},
subscriptions_counter: 1
},
methods: {
fetch_channel_info : function ($channel_id) {
var self = this;
var base_api_url = 'https://www.googleapis.com/youtube/v3/channels';
var query_params = {
'part' : 'snippet,contentDetails',
'key' : 'My-ApiKey',
'maxResults': 1,
'id' : $channel_id
};
var get_params = '';
for (var key in query_params) {
if (get_params != '') {
get_params += '&';
}
get_params += key + '=' + encodeURIComponent(query_params[key]);
}
get_params = '?' + get_params;
axios.get(base_api_url + get_params).then(
function (data) {
data = 'data' in data ? data.data : {};
if (
typeof undefined !== typeof data.items &&
typeof undefined !== typeof data.items[0] &&
typeof undefined === typeof self.channels_info[$channel_id]
) {
var snippet = data.items[0].snippet;
var $key = self.subscriptions_counter + '-' + $channel_id;
self.channels_info[$key] = snippet;
self.subscriptions_counter += 1;
}
}
).catch(
function () {
self.errors.push(
'No channel found matching this channel id.');
}
);
},
// ...
append_to_subscriptions: function ($channel_id) {
if (-1 === this.subscriptions.indexOf($channel_id)) {
this.subscriptions.push($channel_id);
this.fetch_channel_info($channel_id);
// Todo-merianos: Create an AJAX request to set the options in
// database
}
}
}
}
);
};
While my HTML side is like that:
<div class="subscription" v-for="subscription in channels_info">
<span v-text="subscription.title"></span>
</div>
Do you see anything in wrong ? I don't understand why I have that strange output :/
Any sugestion please?
You're appending a new property to an object. I recommend reading this relevant section of the Vue.js documentation regarding object change detection caveats. Specifically, you can use Vue.set(object, key, value) to ensure that your new object key is detected and becomes reactive.
So, instead of self.channels_info[$key] = snippet; you might instead do something like Vue.set(this.channels_info, $key, snippet);.
Definitely read through some more of the documentation. I'm certain that you'll find a lot of value in the rest of the information on this topic.
I have a form and I want to send it to an ajax endpoint, for this I have this helper method:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function(x) { obj[x.name] = x.value; });
return obj;
};
The problem is that I have dot notation in some of the names of my form (using MVC strong typed model)
So I have this object as result:
Task.Addresses.Box:""
Task.Addresses.City:"Limal"
Task.Addresses.Country:"Belgique"
Task.Deadline:"1/10/2017 12:18:18 PM"
Task.TaskSourceId:"1"
And the result expected would be:
{ Task : { Addresses: { Box: "", City: "Limal", Country: "Belgique"}, Deadline: "1/10/2017 12:18:18 PM", TaskSourceId:"1" } }
I use the lodash library but after some hours I cannot find a way to do this expected result.
Can someone provide me a working javascript helper to give the expected nested object?
Edit:
For duplicated question, the other question does not ask about combined nested object together
After the answer of #ori-drori, This code is working as expected:
$.fn.serializeFormToObject = function() {
//serialize to array
var data = $(this).serializeArray();
//add also disabled items
$(':disabled[name]', this)
.each(function() {
data.push({ name: this.name, value: $(this).val() });
});
//map to object
var obj = {};
data.map(function (x) { obj[x.name] = x.value; });
var objNested = {};
_.forEach(obj, function (value, key) { _.set(objNested, key, value) });
return objNested;
};
Iterate the data using Array#forEach, and assign the value to the path (name) using _.set():
data.forEach(function(x) { _.set(obj, x.name, x.value) });
I don't have you're original data, so here is a demo using the key-values you provided.
var fields = {
"Task.Addresses.Box": "",
"Task.Addresses.City": "Limal",
"Task.Addresses.Country": "Belgique",
"Task.Deadline": "1/10/2017 12:18:18 PM",
"Task.TaskSourceId": "1"
};
var obj = {};
_.forEach(fields, function(value, key) {
_.set(obj, key, value);
});
console.log(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
The function that I am working on is getting an input object that has 7 different key-values and each of them could be undefined or not. I want to filter my database based on those key-values that exists in the input. For example if only input.userID exists I want to run this query:
db.query("...WHERE userID = ${userID}", {userID: input.userID});
else if both input.userID and input.startTime exist, I want to do this:
db.query("...WHERE userID = ${userID} and startTime= ${startTime}", {userID: input.userID, startTime: input.startTime});
What I have done is I created a params and keys object like this:
if(input.userID) {
keys.push('userID');
params.push(input.userID);
query = addFilterToTheQuery(query, 'userID', input.userID, filteredItemsCount);
filteredItemsCount = filteredItemsCount +1;
}
addFilterToTheQuery is a simple function I implemented myself. I basically make 7 if cases. Then I have to use those keys and param values to pass to the query function in a way that might need another huge switch case code.
Is this the only way to do this? Is there a better way to get rid of the redundancies in this code?
Custom Type Formatting is the most suitable here.
For example, if we want to convert an object with properties - filter values, we could do it like this:
var pgp = require('pg-promise')(/* initialization options */);
function FilterSet(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this._rawDBType = true; // property renamed later - see UPDATE below
this.formatDBType = function () {
var keys = Object.keys(filters);
var s = keys.map(function (k) {
return pgp.as.name(k) + ' = ${' + k + '}';
}).join(' AND ');
return pgp.as.format(s, filters);
};
}
TEST
var filter = new FilterSet({
first: 1,
second: 'two'
});
var test = pgp.as.format('WHERE $1', filter);
console.log(test);
This outputs:
WHERE "first" = 1 AND "second" = 'two'
If your filters are to be used as %value% with LIKE or ILIKE, then you would need to change your custom type accordingly.
See related questions:
42, 49, 89, 90,
UPDATE
Below is the same example re-written for the latest pg-promise (version 8.x or newer):
const pgp = require('pg-promise')(/* initialization options */);
class FilterSet {
constructor(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this.filters = filters;
this.rawType = true; // do not escape the result from toPostgres()
}
toPostgres(/*self*/) {
// self = this
const keys = Object.keys(this.filters);
const s = keys.map(k => pgp.as.name(k) + ' = ${' + k + '}').join(' AND ');
return pgp.as.format(s, this.filters);
}
}
See Custom Type Formatting.
I'm fairly new to javascript. I retreive data from a sql server database that looks like this :
[Object { shortcode="0013A2004031AC9A", latest_measurement=1067, keyid="6801"},
Object { shortcode="0013A2004031AC9A", latest_measurement=7, keyid="6802"},
Object { shortcode="0013A2004031AC9A", latest_measurement=8598838, keyid="6803"}]
I want to format this in a json like this :
{mac : 0013A2004031AC9A, keys : {6801:1067, 6802:7, 6803:8598838}}
but I just don't get to that.
I have
var jsonDataPerMac = {};
I loop over the json object above and for every new mac I find I do :
jsonDataPerMac[i]={"mac": device.shortcode, "keys":[]};
but how do I get to fill the keys?
Any hints would be appreciated.enter code here
var macs = [];
var jsonDataPerMac = {};
var i = 0;
$.ajax({
url: "/bmmeasurements",
type: "GET",
data: {"unitid" : unitid},
async: false,
success: function (data) {
console.log(data);
initializeTable();
$.each(data, function (index,device) {
//add all distinct macs in an array, to use them as a column header
if($.inArray(device.shortcode, macs) == -1) {
macs.push(device.shortcode);
jsonDataPerMac[i]={"mac": device.shortcode, "keys":[]};
i++;
//create a table cell for each possible key. id = 'mac-key'
createTableGrid(device.shortcode);
}
//add the measurement data to the correct cell in the grid
$('#' + device.shortcode + '-' + device.keyid).html(device.latest_measurement);
});
}});
Here is my proposition. I would rather avoid using jQuery to perform such a simple operations. In this particular example, we use forEach and for..in loop.
//new output array
var newArray = [];
//we traverse the array received from AJAX call
array.forEach(function(el) {
var added = false; // it's false by default
// we check if the mac is already in newArray, if yes - just add the key
for(var i in newArray) {
if(newArray[i].mac == el.shortcode) {
newArray[i].keys.push(el.keyid+":"+el.latest_measurement);
added = true; // tells us whether the key has been added or not
}
}
// if key hasn't been added - create a new entry
if(!added) {
newArray.push({"mac": el.shortcode, "keys":[el.keyid+":"+el.latest_measurement]});
}
});
console.log(newArray);
You can transform above code to a function and then, reuse it in your ajax onSuccess method. Remember to pass the array as an argument and to return newArray.
JSFiddle:
http://jsfiddle.net/2d5Vq/2/
You need to combine the entries first...
var reducedData = {};
$.each(macs, function(index,macitem){
if (reducedData.hasOwnProperty(macitem.shortcode)) {
reducedData[macitem.shortcode].push(macitem.key);
} else {
reducedData[macitem.shortcode] = [ macitem.key ];
}
});
And then map to your desired format inside an array...
var jsonDataPerMac = [],
i = 0;
$.map(reducedData, function(keys,mac){
jsonDataPerMac[i++] = {"mac": mac, "keys": keys};
// your other code goes here
});
Also your usage of jsonDataPerMac suggests that you want it to be an array.
I want to define a Javascript object which manages messages. Within this object, I'll need an array that I can do a push() to:
MsgObjCollection.push(MsgObj)
Essentially I am trying to fill the MsgObjCollection object with a bunch of MsgObjs. Each MsgObj has the 3 variables messagesText, timeStamp, source (sent or received).
Also, I'll need to have some methods like:
MsgObjCollection.Sent.Count // Counts the number of sent messages
MsgObjCollection.Received.Count // Counts the number of received messages
MsgObjCollection.Count // Counts the total number of messages in the object
I'm not sure how to approach this in the simplest, cleanest manner.
NOTE: In case there's any confusion, these are not static methods. I'll be creating instances of these objects using the new operator. So I will need multiple instances.
Here's a tweak on bfavaretto's answer that should get you closer to what you want:
function MsgObjCollection() {
this.sent = [];
this.received = [];
this.total = [];
this.push = function(msg) {
// Assuming msg.source is either 'sent' or 'received',
// this will push to the appropriate array.
this[msg.source].push(msg);
// Always push to the 'total' array.
this.total.push(msg);
};
};
You would use this as follows:
var coll = new MsgObjCollection();
coll.push(/* whatever */);
var sent = coll.sent.length;
var received = coll.received.length;
If you wanted, you could wrap the sent and received arrays with objects that expose a Count function instead of a length property; but that strikes me as unnecessary.
You need push, count, you might want to have all arrays methods / accesssors / iterators.
What's more, you 'll get some speed boost if you let your collection be an array.
So best solution is to inherit from array, and to
have your objects be just real arrays : nothing should be
defined on the object, everything on its prototype.
-->> You'll get the speed and all features of arrays for free.
The function looks like :
function MsgObjCollection() { /* nothing */ };
var SO_pr = ( MsgObjCollection.prototype = [] ) ;
And then, to define count, sent and received on the prototype, use Object.defineProperty not to pollute enumeration, and also to have getters/setters :
Object.defineProperty(SO_pr, 'sent', { get : function() {
var cnt = 0;
this.forEach( function(x) { if (x.source == 'Sent') cnt++; });
return cnt; } } );
Object.defineProperty(SO_pr, 'received', { get : function() {
var cnt = 0;
this.forEach( function(x) { if (x.source == 'Received') cnt++; });
return cnt; } } );
Object.defineProperty(SO_pr, 'count', { get : function() { return this.length } ,
set : function (x) { this.length = x } });
Notice that since the Msg collection's prototype is a new array, you do not pollute array's prototype when changing MsgObjCollection's prototype.
The Sent and Received property you wish are more complex : they act as a view on the underlying object.
One thing you can do is to have them return a new array built out of the right items of the original array.
I prefer, though, to build a wrapper around the original array 1) to allow modification through this view and 2) to avoid garbage creation.
The fiddle is here : http://jsfiddle.net/cjQFj/1/
Object.defineProperty(SO_pr, 'Sent',
{ get : function() { return getWrapper('Sent', this); } } ) ;
Object.defineProperty(SO_pr, 'Received',
{ get : function() { return getWrapper('Received', this); } } ) ;
function getWrapper(wrappedProp, wrappedThis) {
var indx = 0, wp=null;
// try to find a cached wrapper
while (wp = getWrapper.wrappers[indx++] ) {
if (wp.wthis === this && wp.wprop==wrappedProp) return wp.wrapper;
};
// no wrapper : now build, store, then return a new one
var newWrapper = {
get count() { return (wrappedProp=='Sent') ? wrappedThis.sent : wrappedThis.received },
unshift : function () { if (this.count == 0) return null;
var indx=0;
while (wrappedThis[indx].source != wrappedProp ) indx++;
var popped = wrappedThis[indx];
while (indx<wrappedThis.length-1) {wrappedThis[indx]=wrappedThis[indx+1]; indx++; }
wrappedThis.length--;
return popped;
}
};
getWrapper.wrappers.push({ wthis : wrappedThis, wprop : wrappedProp, wrapper : newWrapper });
return newWrapper;
};
getWrapper.wrappers = [];
Now just a little test :
var myColl = new MsgObjCollection();
myColl.push({ source : 'Sent', message : 'hello to Jhon' });
myColl.push({ source : 'Received' , message : 'hi from Kate' });
myColl.push({ source : 'Sent', message : 'hello to Kate' });
myColl.push({ source : 'Received' , message : 'Reply from Jhon' });
myColl.push({ source : 'Received' , message : 'Ho, i forgot from Jhon' });
console.log('total number of messages : ' + myColl.count);
console.log('sent : ' + myColl.sent + ' Sent.count ' + myColl.Sent.count);
console.log('received : ' + myColl.received + ' Received.count ' + myColl.Received.count);
console.log('removing oldest sent message ');
var myLastSent = myColl.Sent.unshift();
console.log ('oldest sent message content : ' + myLastSent.message);
console.log('total number of messages : ' + myColl.count);
console.log('sent : ' + myColl.sent + ' Sent.count ' + myColl.Sent.count);
console.log('received : ' + myColl.received + ' Received.count ' + myColl.Received.count);
Output : >>
total number of messages : 5
sent : 2 Sent.count 2
received : 3 Received.count 3
removing oldest sent message
oldest sent message content : hello to Jhon
total number of messages : 4
sent : 1 Sent.count 1
received : 3 Received.count 3
The annoying part is that those view properties are not arrays, but since you cannot overload [] operator, you cannot have a fully transparent view on the original array, (i.e. : myBox.Sent[i] that would be exactly the i-th sent message ) so at some point you might want to create arrays on the fly for some operations.
There are several ways to do that. One of the simplest, if you only need one instance, is an object literal:
var MsgObjCollection = {
_arr : [],
push : function(val) {
return this._arr.push(val);
},
Sent : {
Count : function() {
// ...
}
},
// etc.
};
If you need multiple instances, use a constructor, and add methods to its prototype property:
function MsgObjCollection() {
this._arr = [];
}
MsgObjCollection.prototype.push = function(val) {
return this._arr.push(val);
}
MsgObjCollection.prototype.get = function(index) {
return this._arr[index];
}
// and so on...
// USAGE:
var collection = new MsgObjCollection();
collection.push('foo');
console.log(collection.get(0));