Anyone knows how can i make requests to twitter api based on text queries without using a recursion.
this is my code
function news_tweets(query, user_id, count) {
news_array = [];
user_tweets = [];
full_array = [];
$.getJSON("https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=false&user_id=" + user_id +
"&count=" + count + "&callback=?",
function (data) {
$.each(data, function (i, item) {
var user = item.user.name;
var date = item.created_at;
var profile_img = item.user.profile_image_url;
var text = item.text;
var url = (item.entities.urls.length > 0 ? item.entities.urls[0].url : '');
news_array.push({
news_user: user,
news_date: date,
news_profile_img: profile_img,
news_text: text,
news_url: url
});
});
find_tweets(news_array);
});
}
function find_tweets(news_array) {
for (var i in news_array) {
var news_text = news_array[i].news_text;
$.getJSON("http://search.twitter.com/search.json?q=" + news_text +
"&rpp=10&include_entities=true&result_type=mixed&callback=?",
function (data) {
$.each(data.results, function (i, item) {
var user = item.from_user;
var user_id = item.from_user_id;
var date = item.created_at;
var user_profile_img = item.profile_image_url;
var text = item.text;
var url = (item.entities.urls.length > 0 ? item.entities.urls[0].url : '');
user_tweets.push({
user: user,
user_id: user_id,
date: date,
user_profile_img: user_profile_img,
text: text
});
});
combine_arrays(news_array, user_tweets);
});
}
function combine_arrays(news_array, user_tweets) {
full_array = news_array.concat(user_tweets); console.log(full_array);
}
}
when i use console.log("hello") or try to connect the two arrays everything is executed three times.
You seem to have only one instance of the news_array and user_tweets arrays. On those, you push all the result of your api queries. Yet, you call the combine_arrays function on the whole arrays from a loop (each time after the search gave you a new set of results) - running multiple times over some of the items.
I guess re-initializing
var user_tweets = [];
inside the find_tweets function would help something.
You can't access the ajax data outside the callback. Instead, you will need to wait until all the asynchronous requests are resolved. I recommend to use jQuery's Deferred object which makes handling such things much easier:
function news_tweets(query, user_id, count) {
var news_array = [],
user_tweets = [];
return $.getJSON("https://api.twitter.com/1/statuses/user_timeline.json", {
include_entities: "true",
include_rts: "false",
user_i: user_id,
count: count
}).then(function (data) {
return $.when.apply(null, $.map(data, function (item) {
news_array.push({
news_user: item.user.name,
news_date: item.created_at,
news_profile_img: item.user.profile_image_url,
news_text: item.text,
news_url: item.entities.urls.length ? item.entities.urls[0].url : ''
});
return $.getJSON("http://search.twitter.com/search.json", {
q: item.text,
rpp: 10,
include_entities: "true",
result_type: "mixed"
}).done(function (data) {
$.each(data.results, function (i, item) {
user_tweets.push({
user: item.from_user,
user_id: item.from_user_id,
date: item.created_at,
user_profile_img: item.entities.urls.length ? item.entities.urls[0].url : '',
text: item.text
});
});
});
}));
}).then(function() {
// this callback is executed [once] when all requests are done
// and the user_tweets array is filled
// arguments is an array of all search request results
var full_array = news_array.concat(user_tweets);
console.log(full_array);
return full_array;
})
}
Usage:
news_tweets(…).done(function callback(full_array) {
// do something with all the objects
});
Related
I've got some IDS that I want to use to fill / generate some jQuery Objects. I use these IDS in ajax requests.
After the jQuery Objects have been filled, I want to sort them.
Has to work in IE11.
What my problem is:
At the moment I've no idea how the best practice would look like, to wait for all ajax requests and as well all jQuery objects to be filled.
I have to wait until all ajax requests have been completed (so
always, indepedent from response code (so on .done and .fail))
I have to wait until all
jQuery Objects are filled with the results from the ajax Requests
Problem conclusion: sort function is called before the items are filled.
I've tried to break down my whole code to a simple example, maybe someone can help here:
function sortEntries(a, b) {
var aa = ($(a).data('name') || '').toLowerCase(),
bb = ($(b).data('name') || '').toLowerCase();
return aa < bb ? -1 : aa > bb ? 1 : 0;
}
function getEntryInfo(infoObj, callback) {
function getLabelByResult(result) {
var name = '';
switch (result) { // For this demo, we don't use the response data.
case 'page-header':
name = 'Hello World 1';
break;
case 'thumbnails':
name = 'It works!';
break;
case 'nav':
name = 'Great!';
break;
case 'btn-groups':
name = 'BTN GROUP';
break;
default:
name = 'NOT SET!'
}
return name;
}
return $.ajax({
method: 'GET',
url: infoObj.URI,
cache: false
}).done(function(data) {
if ($.isFunction(callback)) {
callback(true, {
name: getLabelByResult(infoObj.element)
});
}
}).fail(function() {
if ($.isFunction(callback)) {
callback(false, {
name: getLabelByResult(infoObj.element)
});
}
});
}
function setInfoForEntry(item$, config) {
return getEntryInfo(config, function(isOk, responseObjInfo) {
item$.attr('data-name', responseObjInfo.name || '').text(responseObjInfo.name || '');
});
}
function generateItems() {
var parentItem$ = $('body').append($('<ul/>').addClass('allItems').hide()),
ids = ['page-header', 'thumbnails', 'nav', 'btn-groups'], // for testing purposes of course
requests = [],
extractedItems$;
$.each(ids, function(ignorel, el) {
var newItem$ = $('<li/>').addClass('idItem').on('click', function(e) {
alert($(e.currentTarget).data('name'));
});
parentItem$.append(newItem$);
requests.push(setInfoForEntry(newItem$, {
URI: 'https://getbootstrap.com/docs/3.3/components/#/' + el,
element: el
}));
});
// HERE I HAVE TO ENSURE THAT:
// -ALL AJAX REQUESTS ARE DONE
// -ALL jQuery Elements are filled
extractedItems$ = parentItem$.find('.idItem');
extractedItems$.sort(sortEntries);
extractedItems$.detach().appendTo(parentItem$);
parentItem$.show();
}
$(document).ready(function() {
generateItems();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body></body>
To wait for multiple promises, jQuery has the $.when() utility function. Also see this thread and this thread (there are quite a few posts that discuss this, look around).
I've also streamlined your code a bit, I think it's much easier to read this way:
function by(func) {
return function (a, b) {
var aa = func(a), bb = func(b);
return aa < bb ? -1 : aa > bb ? 1 : 0;
};
}
$(function () {
var things = {
'page-header': 'Hello World 1',
'thumbnails': 'It works!',
'nav': 'Great!',
'btn-groups': 'BTN GROUP'
}, requests;
requests = $.map(Object.keys(things), function (key) {
return $.get('https://getbootstrap.com/docs/3.3/components/#/' + key)
.then(function (data) {
return $('<li class="idItem">', {text: things[key] || 'NOT SET!'})[0];
});
});
$.when.apply($, requests).done(function () {
// each argument here is a single <li> element
var items = $(arguments).sort(by(function (el) {
return ($(el).text() || '').toLowerCase();
}));
$('<ul class="allItems">').appendTo('body').append(items);
}).fail(function (jqXhr, status, error) {
// show error
});
});
loadInfo: function(){
var jsonCounter = 0,
room = ['room1','room2','room3'],
dates = [],
prices = []
$.each(booking.rooms, function(key, room_name) {
$.getJSON('/get_info.php?room='+room_name, function(data) {
dates[room_name] = data
jsonCounter++
})
$.getJSON('/get_info.php?room='+room_name+'&prices', function(data) {
prices[room_name] = data
jsonCounter++
})
})
function checkIfReady() {
if (jsonCounter === rooms.length * 2) {
clearInterval(timer)
run_the_rest_of_the_app()
}
}
var timer = setInterval(checkIfReady, 100)
}
(Modified a lot, as it's part of a class etc etc.)
At the moment this feels a bit hackish, as the timer usage seems rubbish. I would use $.when and $.done, but I don't know how many rooms there might be, so I don't know what to put into when.
How do I ensure that run_the_rest_of_the_app() only gets called once all of the AJAX requests come back?
var activeAJAX = 0;
Before making an AJAX call, activeAJAX++;
After completing an AJAX call (in the callback): if (--activeAJAX == 0) { allDone(); }
Here is how to use when/done
loadInfo: function(){
var room = ['room1','room2','room3'],
dates = [],
prices = [],
requests = [];
$.each(booking.rooms, function(key, room_name) {
var aRequest;
aRequest = $.getJSON('/get_info.php?room='+room_name, function(data) {
dates[room_name] = data;
});
requests.push(aRequest);
aRequest = $.getJSON('/get_info.php?room='+room_name+'&prices', function(data) {
prices[room_name] = data;
});
requests.push(aRequest);
})
$.when.apply($, requests).done(run_the_rest_of_the_app);
}
With my angular application i need to display all indexes on elastic search. I am able to query a particular index using the documentation, not able to get all the indexes on the elastic search.
Here is the Documentation
Here is my code:
$scope.getallIndex = function(){
$scope.Indexes = ejs.Request().query(ejs.MatchAllQuery()
.doSearch().then(function (body) {
$scope.Indexes = body.hits.hits;
console.log($scope.Indexes);
}
const indices = await client.cat.indices({format: 'json'})
I am using elasticsearch.js which is the Browser build for Elastic Search that can be used in the browser . Refer link . I have maintained a factory :
.factory('ElasticService', [ 'esFactory', function (elasticsearch) {
var client = elasticsearch({
host: ip + ':' + port,
});
client.cat.indices("b",function(r,q){
console.log(r,q);
}) }]);
That will return all the indices .Refer link for full configuration.
Edited :
Below is the full factory for retrieving data from a specific index .
.factory('ElasticService', ['$q', 'esFactory', '$location', function ($q, elasticsearch, $location) {
var client = elasticsearch({
host: ip + ':' + port
});
var search = function (index, body) {
var deferred = $q.defer();
client.search({
index: index,
type: type,
body: body
}).then(function (result) {
var took = result.took;
var size = result.hits.total;
var ii = 0, hits_in, hits_out = [],aggs = [], highlight = [];
hits_in = (result.hits || {}).hits || [];
/* For the timebeing i have maintained this variable to save the aggregations */
aggs = result.aggregations;
for (; ii < hits_in.length; ii++) {
hits_in[ii].fields.id = hits_in[ii]._id;
hits_out.push(hits_in[ii].fields);
// hits_out[hits_in[ii]._id]=hits_in[ii].fields: use this method if you wanna keep _id as the index
// if there is a highlight coming along with the data we add a field highlight and push it to built an array
if (hits_in[ii].highlight) {
highlight.push(hits_in[ii].highlight);
}
}
if (highlight.length) {
deferred.resolve({hits: hits_out, highlight: highlight,aggs:aggs, size: size, took: took});
}
else {
deferred.resolve({hits: hits_out, size: size,aggs:aggs, took: took});
}
}, deferred.reject);
return deferred.promise;
};
return {search: search}; }]);
And so the controller you can use this factory for fetching data from a particular index
ElasticService.search('<index>', <query>).then(function (resp) {
// resp has the content
});
I did query like below in javascript and it returned me index list
client.cat.indices({
h: ['index']
}).then(function (body) {
console.log(body);
});
The elasticsearch JS API has a method to query all the indices on the instance: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html#api-cat-indices
Only thing is that it doesn't really return to you in a readable JSON format. So what I end up doing is something like the following
client.cat.indices({
h: ['index', 'docs.count']
}).then(function (body) {
let lines = body.split('\n');
let indices = lines.map(function (line) {
let row = line.split(' ');
return {name: row[0], count: row[1]};
});
indices.pop(); //the last line is empty by default
});
I am trying to design a personal app which loads data asynchronously and then displays a grid according to the windows 8.1 store apps.
i'm running into the issue that my ui is trying to execute before my data is loaded.
my current code:
(function () {
"use strict";
var asyncInProgress = true;
var groupedItems;
var list;
var observable;
var matches = new WinJS.Binding.List();
var matchGroups = new WinJS.Binding.List();
var BattleGrounds = new WinJS.Binding.List();
list = getData();
initGroups(list);
function initGroups(l) {
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
}
WinJS.Namespace.define("Data", {
Observable: WinJS.Class.define(function () {
this.dispatch = function () {
this.dispatchEvent("dataReady");
}
}),
getObservable: getObservable,
items: groupedItems,
groups: groupedItems.groups,
getItemReference: getItemReference,
getItemsFromGroup: getItemsFromGroup,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference,
updateData: updateData,
getAsyncStatus: getAsyncStatus
});
WinJS.Class.mix(Data.Observable, WinJS.Utilities.eventMixin);
WinJS.Class.mix(Data.Observable, WinJS.Utilities.createEventProperties("dataReady"));
// Provides support for event listeners.
function getObservable() {
observable = new Data.Observable();
return observable;
}
// Get a reference for an item, using the group key and item title as a
// unique reference to the item that can be easily serialized.
function getItemReference(item) {
return [item.group.key, item.title, item.backgroundImage];
}
// This function returns a WinJS.Binding.List containing only the items
// that belong to the provided group.
function getItemsFromGroup(group) {
return list.createFiltered(function (item) { return item.group.key === group.key; });
}
// Get the unique group corresponding to the provided group key.
function resolveGroupReference(key) {
return groupedItems.groups.getItemFromKey(key).data;
}
// Get a unique item from the provided string array, which should contain a
// group key and an item title.
function resolveItemReference(reference) {
for (var i = 0; i < groupedItems.length; i++) {
var item = groupedItems.getAt(i);
if (item.group.key === reference[0] && item.title === reference[1]) {
return item;
}
}
}
function updateData() {
asyncInProgress = true;
BattleGrounds.splice(0, matches.length);
BattleGrounds._currentKey = 0;
groupedItems = null;
list = getData();
initGroups(list);
}
function getAsyncStatus() {
return asyncInProgress;
}
function getData() {
var darkGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY3B0cPoPAANMAcOba1BlAAAAAElFTkSuQmCC";
var lightGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC";
var mediumGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
var url = 'https://api.guildwars2.com/v1/wvw/matches.json';
acquireSyndication(url).then(function (response) {
// Remove any invalid characters from JSONp response.
var fixedResponse = response.responseText.replace(/\\'/g, "'");
var jsonObj = JSON.parse(fixedResponse);
jsonObj.wvw_matches.forEach(function (battle) {
var anet_id = value.wvw_match_id;
// Create Group
var matchGroup = {
key: anet_id,
title: anet_id
};
matchGroups.push(matchGroup);
// Get Details
acquireSyndication("https://api.guildwars2.com/v1/wvw/match_details.json?match_id=" + anet_id).then(function (json) {
var fixedJson = json.responseText.replace(/\\'/g, "'");
var obj = JSON.parse(fixedJson);
fixedJson.maps.forEach(function (value) {
BattleGrounds.push({
group: matchGroup, key: matchGroup.title, title: value.type,
subtitle: value.type, map: "eb", description: "NA", content: "NA", "type": value.type,
"scores": value.scores, "objectives": value.objectives, "bonuses": value.bonuses, backgroundImage: lightGray
});
});
}, function (error) {
var x = error.getAllResponseHeaders();
var matchGroup = matchGroups[0];
for (var i = 0; i < matchGroups.length; i++) {
flickrPosts.push({
group: matchGroups[i], key: matchGroup.title, title: "Error loading",
subtitle: "Error", backgroundImage: lightGray, published: "N/A", description: "N/A"
});
}
asyncInProgress = false;
observable.dispatch();
});
});
}, function (error) {
var x = error.getAllResponseHeaders();
var matchGroup = matchGroups[0];
for (var i = 0; i < matchGroups.length; i++) {
flickrPosts.push({
group: matchGroups[i], key: matchGroup.title, title: "Error loading",
subtitle: "Error", backgroundImage: lightGray, published: "N/A", description: "N/A"
});
}
asyncInProgress = false;
observable.dispatch();
});
return BattleGrounds;
}
function acquireSyndication(url) {
return WinJS.xhr({
url: url,
headers: { "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT" }
});
}
})();
This errors out on groups: groupedItems.groups. which says that groups is undefined.
i know this is because the data is still being processed.
How am i going to work around this?
i took a look at the promise object but the entire concept confuses me as i don't know enough about the infrastructure of a windows 8 app.
The core of your problem is in the getData() function - it is not returning your data because it uses asynchronous calls to get the data. The data is not yet available when it returns. It appears that that function makes several asynchronous calls to get data (using acquireSyndication()). When those asynchronous functions finish sometime in the future, you then put that data into matchGroups and then later into BattleGrounds after more calls to acquireSyndication().
What you're doing is quite messy so there isn't a simple fix. Conceptually, you need to process the BattleGrounds data from the completion handler of the asynchronous code and ALL code that uses it must continue from inside that completion handler, not after the getData() call. You cannot call getData() and use it like a synchronous function because it's asynchronous. This requires asynchronous programming techniques.
If you are doing multiple asynchronous calls and trying to carry out some action after all of them have completed (which I think is what you're doing), then you will need to code specifically for that condition too. You can either use promises or you can keep a counter of how many ajax calls there are and in each completion function, you increment the counter and see if this is the last one that just completed and, if so, then you can process all the data and continue executing the rest of your code.
I would also suggest that you don't use promises in one part of a function and then completion callbacks in the very next part. Use one of the other, not a mixture, to keep your code clean.
I have two observable arrays:
var viewModel = {
PositionTypes: ko.observableArray([]),
Users: ko.observableArray([])
}
POSITION ViewModel
var positionViewModel = function (data) {
var _self = this;
_self.PositionName = ko.observable(data.PositionName);
_self.PositionRank = ko.observable(data.PositionRank);
_self.ContentRole = ko.observable(data.ContentRole);
}
positionViewModel.AddPositions = function (data) {
$.each(data, function (index, value) {
positionViewModel.PushPosition(value);
});
};
positionViewModel.PushPosition = function (postion) {
viewModel.PositionTypes.push(new positionViewModel(position));
};
USER ViewModel
// the ViewModel for a single User
var userViewModel = function (data) {
var _self = this;
_self.ID = ko.observable(data.ID);
_self.Name = ko.observable(data.Name);
_self.Email = ko.observable(data.Email);
_self.ContentRole = ko.observable(data.ContentRole);
};
userViewModel.AddUsers = function (data) {
$.each(data, function (index, value) {
userViewModel.PushUser(value);
});
};
userViewModel.PushUser = function (user) {
viewModel.Users.push(new userViewModel(user));
};
How can i using linq.js so that i could loop through every position so i could get all the users for each position?
foreach( each position in positions)
{
foreach(each user in users)
{ list of users for the position}
}
You could also use ko.utils.arrayForEach as follow :
ko.utils.arrayForEach(viewModel.PositionTypes(), function(position){
var usersInPosition = ko.utils.arrayFilter(viewModel.Users(), function(user){
return user.ContentRole() == position.ContentRole();
});
ko.utils.arrayForEach(usersInPosition, function(user){
});
});
See doc
I hope it helps.
Using linq.js, you can perform a join on the columns you want to compare.
Assuming you are joining between the ContentRoles:
var query = Enumerable.From(viewModel.PositionTypes())
.GroupJoin(viewModel.Users(),
"$.ContentRole()", // position selector
"$.ContentRole()", // user selector
"{ Position: $, Users: $$.ToArray() }")
.ToArray();
So I think you want to create an object that contains a mapping of all the positions and user names. You can create such an object using the Aggregate() function to collect all the results into a single object.
var userPositions = Enumerable.From(this.PositionTypes())
.GroupJoin(this.Users(),
"$.ContentRole()", // position selector
"$.ContentRole()", // user selector
"{ Position: $, Users: $$ }") // group all users per position
.Aggregate(
{}, // start with an empty object
function (userPositions, x) {
var positionName = x.Position.PositionName(),
userNames = x.Users.Select("$.Name()").ToArray();
// add the new property
userPositions[positionName] = userNames;
return userPositions;
}
);