I am trying to push array content in a global defined empty array & then retrieve its contents inside another function.
Below is the code which i tried:
describe('My Test', function() {
var arrayf3=[];
var indexf3='not found';
it('Test starts', function() {
browser.ignoreSynchronization = true;
browser.get('https://www.w3schools.com/angular/');
var elm = element(by.id('leftmenuinner')).all(By.css('[target="_top"]'));
elm.count().then(function(count) {
Methods.pushToArray(0, count, elm);
})
var texttocheck='Data Binding';
Methods.getIndex(0, arrayf3.length, arrayf3, texttocheck);
console.log('Text content of global array is ' + arrayf3);
console.log('index of the array number having texttofind is ' + indexf3);
})
var Methods = {
getIndex :function (i, max, array, texttocheck) {
if (i < max) {
console.log('text[' + i + '].indexOf = ' + array[i].indexOf(texttocheck))
if (array[i].indexOf(texttocheck) > 0) {
indexf3 = i;
} else {
Methods.getIndex(i + 1, max, array, texttocheck);
}
}
},
pushToArray :function (i, max, elm) {
if (i < max) {
elm.get(i).getText().then(function(tmpText) {
console.log("The array "+tmpText);
arrayf3.push(tmpText);
})
Methods.pushToArray(i + 1, max, elm);
}
},
}
});
Problem is i am getting null values for below placeholder values:
Text content of global array is
index of the array number having texttofind is
I want the array value copied in this global empty array to be used & displayed in the same it block function 'Test starts'
Protractor's element.all inherently knows how to getText() on each of the elements and return the values as an array.
it('Test starts', function() {
browser.ignoreSynchronization = true;
browser.get('https://www.w3schools.com/angular/');
var getIndexOfElementByPartialText = function(inputText) {
return element(by.id('leftmenuinner')).all(by.css('[target="_top"]')).getText().then(function(values) {
var indexNumber;
values.forEach(function(value, index) {
if (new RegExp(inputText).test(value)) {
if (indexNumber === undefined) {
indexNumber = index;
} else {
throw new Error('multiple elements match the input text');
}
}
});
if (indexNumber === undefined) {
throw new Error('no elements match the input text');
} else {
return indexNumber;
}
});
});
expect(getIndexOfElementByPartialText('thing1').toBe(1);
expect(getIndexOfElementByPartialText('thing2').toBe(2);
});
Edited the answer to provide it in terms of a re-usable function.
Related
I have a function that loops through in indeterminate number of items and does an asynchronous call on each one to get additional data (the content of html template files). The callback does some checking. The resulting function should be thenable. $q is injected earlier, this code is part of a factory.
function searchHelpTopics(topics, searchPhrase) {
if (topics == null || topics.length == 0) return "No search results";
var results = [];
var promises = [];
for (var i = 0; i < topics.length; i++) {
var templateURL = topics[i].URL;
var topic = topics[i];
if (topics[i].HelpTopicId != "Search") {
var promise = $templateRequest(templateURL).then(function (template) {
var text = HTMLToText(template, true);
// do the search
if (text.indexOf(searchPhrase) > -1) {
if (text.length > 50) text = text.substring(0, 50);
var result = {};
result.title = topic.Title;
result.excerpt = text;
result.helpID = topic.HelpTopicID;
results.push(result);
}
});
promises.push(promise);
}
}
return $q.all(promises).then(function () {
return results;
})
The problem here is that the for loop does not wait for the callbacks obviously and so the topic being used by the callback is not the correct one. I need a way to pass topic into the callback on each loop.
Because JS has only function scope you can rewrite your code to use function instead of 'for' loop (which is usually better).
To do that you can use JS built-in forEach (which is available starting from version 1.6 so almost for all browsers) or good functional style libraries like underscore.js or lodash.js.
Or even better - to use Array.map and Array.filter - see the code
function processTemplate(topic, template) {
var text = HTMLToText(template, true);
// do the search
if (text.indexOf(searchPhrase) < 0) {
return;
}
if (text.length > 50) {
text = text.substring(0, 50);
}
return {
title: topic.Title,
excerpt: text,
helpID: topic.HelpTopicID
};
}
function searchHelpTopics(topics, searchPhrase) {
if (!topics || topics.length === 0) {
return "No search results";
}
var promises = topics
.filter(function(topic) { return topic.HelpTopicId !== "Search"; })
.map(function(topic) {
return $templateRequest(topic.URL).then(processTemplate);
});
return $q.all(promises)
.then(function (results) {
return results.filter(function (result) {
return result; // filters out 'undefined'
});
});
}
The is not a complete solution but enough to indicate how it works
somefactory.getHelpTopics().then(function (topics) {
somefactory.searchHelpTopics(topics, searchText).then(function (searchResults) {
vm.searchResults = searchResults;
vm.helpID = "Search";
});
});
--- some factory functions ----
function searchHelpTopics(topics, searchPhrase) {
if (!topics || topics.length === 0) return "No search results";
var promises = topics
.filter(function (topic) { return topic.HelpTopicId !== "Search"; })
.map(function (topic) {
return $templateRequest(topic.URL).then(function (template) {
return searchHelpTemplate(template, topic, searchPhrase);
});
});
return $q.all(promises).then(function (results) {
return results.filter(function (result) {
return result; // filters out 'undefined'
});
});
}
function searchHelpTemplate(template, topic, searchPhrase) {
var text = HTMLToText(template, true);
// do the search
if (text.indexOf(searchPhrase) < 0 && topic.Title.indexOf(searchPhrase) < 0) {
return;
}
if (text.length > 50) {
text = text.substring(0, 50);
}
return {
title: topic.Title,
excerpt: text,
helpID: topic.HelpTopicId
};
}
I'm making a simple twitter app to work on my javascript.
The code below is supposed to identify every tweets location and count the number of tweets per location.
However, it doesn't increment, it just creates a new array.
What is wrong with my code? How can I make it better?
Thank you
var Twitter = require('node-twitter'),
twit = {},
loc = [];
twit.count = 0;
var twitterStreamClient = new Twitter.StreamClient(
//credentials
);
twitterStreamClient.on('close', function () {
console.log('Connection closed.');
});
twitterStreamClient.on('end', function () {
console.log('End of Line.');
});
twitterStreamClient.on('error', function (error) {
console.log('Error: ' + (error.code ? error.code + ' ' + error.message : error.message));
});
twitterStreamClient.on('tweet', function (tweet) {
if (loc.indexOf(tweet.user.location) === -1) {
loc.push({"location": tweet.user.location, "locCount": 1});
} else {
loc.loation.locCount = loc.loation.locCount + 1;
}
console.log(loc);
});
var search = twitterStreamClient.start(['snow']);
You need to rewrite on tweet callback:
var index = loc.reduce(function(acc, current, curIndex) {
return current.location == tweet.user.location ? curIndex : acc;
}, -1);
if (index === -1) {
loc.push({"location": tweet.user.location, "locCount": 1});
} else {
loc[index].locCount++;
}
Array.indexOf is not matching as you think it is. You're creating a new object and pushing it into the array, and regardless of whether its properties match a different object perfectly, it will not be === equal. Instead, you have to find it manually:
var foundLoc;
for (var i = 0; i < loc.length; i++) {
if (loc[i].location.x === location.x)
foundLoc = loc[i];
break;
}
}
if (!foundLoc) {
loc.push({location: location, count: 0});
} else {
foundLoc.count++
}
I'm creating a self updating match/fixture script, which return data from a JSON Script which automatically updates. However the JSON script include 3 arrays live, upcoming and recent. How can i loop through all of these arrays instead of just matches[0] which is live? i've tried using loop, but it cant loop through the functions?
var lastLoadedMatch = 0,
ajaxInterval = 5000;
$(function() {
getMatches();
});
function getMatches(lastId) {
$.getJSON('data.json', function(resp) {
console.log(JSON.stringify(resp));
var matches = [resp.live, resp.upcoming, resp.recent];
if (lastId) {
matches[0] = matches[0].filter(
function(match) {
return match.id > lastId;
});
}
if (matches[0].length) {
$.each(matches[0], function(_, match) {
console.log([match.match_id,lastLoadedMatch ])
if (match.match_id > lastLoadedMatch) {
lastLoadedMatch = match.match_id
}
if ($.trim(match.game) == "lol") {
$('#part-1').append(matchHtml(this))
} else if ($.trim(match.game) == "counterstrike") {
$('#part-2').append(matchHtml(this))
} else if ($.trim(match.game) == "dota2") {
$('#part-3').append(matchHtml(this))
} else if ($.trim(match.game) == "hearthstone") {
$('#part-4').append(matchHtml(this))
}
});
}else{
}
setTimeout(function() {
getMatches(lastLoadedMatch);
}, ajaxInterval);
});
}
Just wrap your code in one more $.each that will iterate of your matches array:
var matches = [resp.live, resp.upcoming, resp.recent];
$.each(matches, function(_, match) {
if (lastId) {
match = match.filter(
function(match) {
return match.id > lastId;
});
}
if (match.length) {
$.each(match, function(_, match) {
...
or you can concatenate the arrays:
var matches = resp.live.concat(resp.upcoming, resp.recent);
So basically I have some promise, forEach, just a lot of issues with this single problem I need to solve. So the variables I work with has below structure:
persons = [object, object, object]
where each object has { user:number , username: string, latitude:number, longitude:number}
from there I try to figure out if my user/username is inside of one of these objects, if not found id like it to be created, if found found id like it to update their location. Sounds simple, I think the problem has blown out of proportion but nothing works. The code I have now does not work, its either I can never figure out when the user is not there, or I can not figure out how to get to stop creating me every time it find a user who is not me.
var getswamp = function(item, index) {
return new Promise(function(resolve, reject){
var result = false;
if (item.user === user && item.username === username) {
if ((item.latitude !== latitudenew) || (item.longitude !== longitudenew)) {
var id = item.id;
swampdragon.update('locationcurrent', {
user: user,
latitude: latitudenew,
longititude: longitudenew,
username: username,
id: id
}, function (context, data) {
console.log("data updated", data);
result = true;
resolve(result);
}, function (context, data) {
console.log("You may not be updated");
});
} else {
console.log("No location change");
result = true;
}
}else{
if ( item.index === person.index){
console.log(person);
resolve(result)
}
}
});
};
person.forEach(function (item, index) {
var swamping = getswamp(item, index);
swamping.then(function (result) {
console.log(result);
if (result === true) {
console.log("We have you");
} else if (result === false && (index === person.length - 1)) {
console.log('Index: ' + index + ' Length of list is ' + person.length);
swampdragon.create('locationcurrent', {
user: user,
latitude: latitudenew,
longititude: longitudenew,
username: username
}, function (context, data) {
console.log("data created", data);
}, function (context, data) {
console.log("You may not be created")
});
}
})
});
Any help/ideas would just be great.
The Promise is used when some asynchronous event happened.
Since I can not create such event, I made a static code as below:
var persons = new Array();
persons.indexOf = function(person) {
var index = -1;
this.forEach(function(obj, i) {
if (person.username == obj.username) {
index = i;
}
});
return index;
}
persons.addOrUpdate = function(person) {
var index = this.indexOf(person);
if (index == -1) {
person.user = this.length + 1;
this.push(person);
}
else { // update case
var obj = this[index];
obj.latitude = person.latitude;
obj.longitude = person.longitude;
}
}
persons.print = function() {
var str = "";
this.forEach(function(obj) {
str += obj.user + ". " + obj.username + " at location (" +
obj.latitude + ":" + obj.longitude + ")\n";
});
return str;
}
persons.addOrUpdate({username:'Adam', latitude:-0.0045, longitude:14.2015});
persons.addOrUpdate({username:'Eve', latitude:-0.0045, longitude:14.2015});
persons.addOrUpdate({username:'Abel', latitude:-0.0045, longitude:14.2015});
// Updating Able location
var person = {username:'Abel', latitude:10.1145, longitude:14.1234};
persons.addOrUpdate(person);
alert(persons.print());
I have a code like this
(function($, window, document, undefined) {
$.fn.quicksearch = function (target, opt) {
var timeout, cache, rowcache, jq_results, val = '', e = this, options = $.extend({
delay: 100,
selector: null,
stripeRows: null,
loader: null,
noResults: '',
bind: 'keyup',
onBefore: function () {
return;
},
onAfter: function () {
return;
},
show: function () {
this.style.display = "";
},
hide: function () {
this.style.display = "none";
},
prepareQuery: function (val) {
return val.toLowerCase().split(' ');
},
testQuery: function (query, txt, _row) {
for (var i = 0; i < query.length; i += 1) {
if (txt.indexOf(query[i]) === -1) {
return false;
}
}
return true;
}
}, opt);
this.go = function () {
var i = 0,
noresults = true,
query = options.prepareQuery(val),
val_empty = (val.replace(' ', '').length === 0);
for (var i = 0, len = rowcache.length; i < len; i++) {
if (val_empty || options.testQuery(query, cache[i], rowcache[i])) {
options.show.apply(rowcache[i]);
noresults = false;
} else {
options.hide.apply(rowcache[i]);
}
}
if (noresults) {
this.results(false);
} else {
this.results(true);
this.stripe();
}
this.loader(false);
options.onAfter();
return this;
};
this.stripe = function () {
if (typeof options.stripeRows === "object" && options.stripeRows !== null)
{
var joined = options.stripeRows.join(' ');
var stripeRows_length = options.stripeRows.length;
jq_results.not(':hidden').each(function (i) {
$(this).removeClass(joined).addClass(options.stripeRows[i % stripeRows_length]);
});
}
return this;
};
this.strip_html = function (input) {
var output = input.replace(new RegExp('<[^<]+\>', 'g'), "");
output = $.trim(output.toLowerCase());
return output;
};
this.results = function (bool) {
if (typeof options.noResults === "string" && options.noResults !== "") {
if (bool) {
$(options.noResults).hide();
} else {
$(options.noResults).show();
}
}
return this;
};
this.loader = function (bool) {
if (typeof options.loader === "string" && options.loader !== "") {
(bool) ? $(options.loader).show() : $(options.loader).hide();
}
return this;
};
this.cache = function () {
jq_results = $(target);
if (typeof options.noResults === "string" && options.noResults !== "") {
jq_results = jq_results.not(options.noResults);
}
var t = (typeof options.selector === "string") ? jq_results.find(options.selector) : $(target).not(options.noResults);
cache = t.map(function () {
return e.strip_html(this.innerHTML);
});
rowcache = jq_results.map(function () {
return this;
});
return this.go();
};
this.trigger = function () {
this.loader(true);
options.onBefore();
window.clearTimeout(timeout);
timeout = window.setTimeout(function () {
e.go();
}, options.delay);
return this;
};
this.cache();
this.results(true);
this.stripe();
this.loader(false);
return this.each(function () {
$(this).bind(options.bind, function () {
val = $(this).val();
e.trigger();
});
});
};
}(jQuery, this, document));
I try to figure out where and how I can make a split/add space between numbers and letters. Cause some people type for example "ip1500" and the script cant match the input with an element that is like "ip 1500". My problem ist that Im a js beginner.
I was trying and trying but i cant get it work. I also tried this
I found this spot and I think it can be done here where the everything get splitted by an " " (space):
prepareQuery: function (val) {
return val.toLowerCase().split(' ');
},
Would be very nice if somebody can help me.
If you want "123abc345def" to "123 abc 345 def". The replace function may help. The code is like this.
var str = "123abc345def";
str = str.replace(/(\d+)/g, function (_, num){
console.log(num);
return ' ' + num + ' ';
});
str = str.trim();
The code you linked didn't work mainly because it's using a different programming language to javascript. In theory, it should work, but javascript does not support regular expression lookbehinds (at this present time)..
Instead, I have re-wrote that fragment of code:
prepareQuery: function (val) {
function isNotLetter(a){
return (/[0-9-_ ]/.test(a));
}
var val=val.toLowerCase().split("");
var tempArray=val.join("").split("");
var currentIndex=1;
for (var i=0;i<val.length-1;i++){
if (isNotLetter(val[i]) !== isNotLetter(val[i+1])){
tempArray.splice(i+currentIndex, 0, " ");
currentIndex++;
}
}
return tempArray.join("");
}
Since you're new to javascript, I'm going to explain what it does.
It declares a function in prepareQuery to check whether or not a string contains a letter [this can be moved somewhere else]
It then splits val into an array and copies the content of val into tempArray
An index is declared (explained later)
A loop is made, which goes through every single character in val
The if statement detects whether or not the current character (val[i] as set by the loop) is the same as the character next to it (val[i+1]).
IF either one are different to the other (ie the current character is a letter while the next isn't) then a space is added to the tempArray at that "index"
The index is incremented and used as an offset in #6
The loop finishes, joins the "array" into a string and outputs the result.
DEMO:
http://jsbin.com/ebitus/1/edit
(JSFiddle was down....)
EDIT:
Sorry, but I completely misinterpreted your question... You failed to mention that you were using "quicksearch" and jQuery. In that case I'm assuming that you have a list of elements that have names and you want to search through them with the plugin...
A much easier way to match the user's query (if there is no space) is to strip the space from the search table along with the query itself - though original reverse method will work (just not as efficiently) [aka: expanding the user's query]
In this case, stripping the space from both the search table and user input would be a better method
prepareQuery: function (val) {
return val.toLowerCase().replace(/ /ig,'').split(" ");
},
testQuery: function (query, txt, _row) {
txt=txt.toLowerCase().replace(/ /ig,'');
for (var i = 0; i < query.length; i += 1) {
if (txt.indexOf(query[i]) === -1) {
return false;
}
}
return true;
}
DEMO:
http://jsfiddle.net/q9k9Y/3/
Edit 2:
It seems like your real intent is to create a fully functioning search feature on your website, not to just add spaces between letters and numbers. With this, I suggest using Quicksilver. I would love to work out an algorithm to extend quickSearcher but at the current time I cannot (timezones). Instead, I suggest using Quicksilver
http://jsbin.com/oruhet/12/