how to check a string against elements in an array - javascript

I am using the twitter stream api. I am currently using follow & track parameters but the API combines them with an OR and I want to combine them with an AND. The only way logically to do this is to manually check that the tweet contains a userID# in my array of userIDs to follow and the tweet text contains a keyword from my array of keywords to track.
I have converted the tweet object and tweet.text object into a JSON string like this:
var tweetText = JSON.stringify(tweet.text);
var tweetObject = JSON.stringify(tweet);
I want an if statement like this:
if tweetObject == a value in tracked ids array && tweetText == a value in tracked words array
do the rest of my code
How can I achieve this? I tried to use .indexOf() but that takes only one parameter so I could say:
if(tweetObject.indexOf("12345678") > -1 && tweetText.indexOf("spotify") > -1) {
do my code
}
But this is NOT what I want, I want it to go through the array and see if tweetObject and tweetText contain any of the array elements and if so do the code
this is what I have:
// start twitter
t.stream(
"statuses/filter",
{follow: trackedHandles, track: trackedWords, lang: "en" },
function(stream) {
stream.on('data', function(tweet) {
//convert tweet.text& tweet.entities into two strings
var tweetText = JSON.stringify(tweet.text);
var userName = JSON.stringify(tweet.entities);
var tweetObject = JSON.stringify(tweet);
var flag = false;
for (var x = 0; x < trackedHandles.length; x++) {
//console.log(trackedHandles[x].toString());
var searchParam = tweetObject.indexOf(trackedHandles[x].toString());
if(searchParam != -1) {
flag = true;
//console.log(searchParam);
//console.log(trackedHandles[x].toString());
//incriment the keywords, and store the keywords as keys in redis (as they appear in feed)
for (var i = 0; i < trackedWords.length; i++) {
if(tweetText.indexOf(trackedWords[i]) > - 1) {
// incriments added value to the word
console.log(trackedWords[i]);
redisClient.incr(trackedWords[i]);
}
}
//if tweetText does not contains "RT" & "#", print tweet to console.
if(tweetText.indexOf("RT") == -1 && tweetText.indexOf("#") == -1) {
//console.log(tweetText + "\r\n ");
//console.log(screen_name + "\r\n ");
//console.log(tweet);
}
}
}
});
}
);
Please help guys, this is for my senior project for my undergrad degree. I have most of the app it self complete, I just need this one piece because What I am trying to accomplish is to only stream tweets from specific users based on specific keywords. If you can think of a more appropriate title for this please let me know. Thanks in advance for any help!

Use a for or forEach loop
var tweetObjArr= [1,2,3,4,5];
var tweetIds = [7,3]
var flag = false;
for (var i = 0; i < tweetIds .length; i++) {
if(tweetObjArr.indexOf(tweetIds [i]) != -1) {
flag=true;
break;
}
}

Might be you want jQuery.inArray function like
if(jQuery.inArray("12345678", tweetObject)!==-1)

Related

Listen to element - call function when another element is appended to it

Intro
I have a search bar I implemented into my website which searches through member cards to find matching cards. I also used Twitter's typeahead.js for this. The results are updated as you type, so I set an event listener on the input box - $('#members-search .typeahead').on("input", changeFunction); I also needed to set a click event listener on the suggestions, as I did - $('.tt-suggestion').on("click", changeFunction);
Problem
It seems like the suggestion boxes are created on the fly, so you can't set an event listener for all (or even any!) of them at the beginning. My first idea was to fire a function when an element was appended in the containing div. However, you would need an event listener for that, and I couldn't find one. Is there any way to implement this?
Code
The JavaScript:
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var children = document.getElementById("members-list").children;
var names = [];
var whoIsWho = [];
var selected = [];
var listOfAttributeNames = ["data-member-name", "data-member-username", "data-member-nickname"];
for(var i = 0; i < children.length; i++){
for(var j = 0; j < listOfAttributeNames.length; j++){
var a;
if(a = children[i].getAttribute(listOfAttributeNames[j])){
names.push(a);
whoIsWho.push(children[i]);
}
}
}
$('#members-search .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'names',
source: substringMatcher(names)
});
var previousValue = "";
function changeFunction(e){
var v;
if($("#members-search .typeahead").val() === ""){
previousValue = "";
}
else if(((v = $('#members-search .typeahead+pre').text())) !== previousValue){
previousValue = v;
}
else if(v !== $("#members-search .typeahead").val()){
previousValue = $("#members-search .typeahead").val();
}
selected = [];
v = $('#members-search .typeahead+pre').text();
for(var i = 0; i < names.length; i++){;
if(!(new RegExp(v, "i").test(names[i])) && !(selected.includes(whoIsWho[i]))){
whoIsWho[i].style.display = "none";
}
else{
selected.push(whoIsWho[i]);
whoIsWho[i].style.display = "block";
}
}
}
$('#members-search .typeahead').on("input", changeFunction);
$('.tt-suggestion').on("click", changeFunction);
The (important) HTML:
<div id="members-search">
<input class="typeahead" type="text" placeholder="Search">
</div>
Alternate, Backup Solutions
I could copy the bloodhound script over to my code and modify where the elements are appended, but I'd rather not, as it uses this weird format of IIFE that I won't take the time to understand. Or is there another solution and this question is part of the X/Y problem?
It turns out I had the wrong approach. I just added an event listener to the current suggestions every time the input value was changed.
$('#members-search .typeahead').on("input", function(){
$('.tt-suggestion').on("click", changeFunction);
});

Collect unique email addresses from Gmail threads

I'm new to Google Apps Script and I'm trying to make a script for a spreadsheet where I'll store all the email addresses found by .getFrom() method in the sheet and ignore the same email addresses so that I get only one email address instead of multiple times. So far storing is working successfully but ignoring same emails is not working. I get same emails multiple times in my sheet's column.
Here's my code:
var n=threads.length;
var messages=thread.getMessages();
var getfrom = 0;
var allMails = [];
for (var i=0; i<n; i++)
{
for (var j=0; j<messages.length; j++)
{
var message=messages[j];
getfrom = message.getFrom();
var first_name = getfrom.substring(0, getfrom.indexOf(" "));
var last_name = getfrom.substring(getfrom.indexOf(" ")+1, getfrom.indexOf(" <"));
var email_address = 0;
if (first_name == '' && last_name == '')
{
email_address = getfrom;
} else {
email_address = getfrom.substring(getfrom.indexOf("<")+1, getfrom.indexOf(">"));
}
// This is how I check if I already have the email address or not
if (email_address == my_email || email_address[j] == email_address[j-1])
{
continue;
}
}
allMails.push([email_address]);
}
Logger.log(allMails);
sheet1.getRange(2, 3, n, 1).setValues(allMails);
Browser.msgBox("Operation complete");
How can I ignore duplicate values and get one email address instead of multiple times?
You can either ensure uniqueness before adding emails to the list, or build the full list first and remove duplicates later.
Option 1: Pre-filter
This example builds a one-dimensional array of addresses; because it's a simple array we can use the JavaScript built-in .indexOf() method to check for uniqueness. After all threads have been examined, the simple array is converted to a two-dimensional array for storage in the spreadsheet, using another Array built-in, map(). Before that though, the array gets sorted - just because we can. You might want to do other filtering, such as removing "no-reply" addresses.
function getUniqueFromAddresses1() {
var my_email = Session.getActiveUser().getEmail();
var threads = GmailApp.getInboxThreads();
var n=threads.length;
var allMails = [];
for (var i=0; i<n; i++)
{
var thread = threads[i];
var messages=thread.getMessages();
for (var j=0; j<messages.length; j++)
{
var message=messages[j];
var getfrom = message.getFrom();
// Use RegEx to extract just email address
var email_address = getfrom.match(/[^<> ]*\#[^> ]*/)[0];
// Skip messages I sent or addresses already collected
var index = allMails.indexOf(email_address);
if (email_address !== my_email && allMails.indexOf(email_address) == -1) {
allMails.push(email_address);
}
}
}
// Could do further filtering & sorting of allEmails here
allMails = allMails.sort()
Logger.log(JSON.stringify(allMails));
// convert allMails array to two-dimensional array
allMails = allMails.map( function(item){
return [item];
});
Logger.log(JSON.stringify(allMails));
// Store in spreadsheet; use dimensions of array to avoid mismatching range size
sheet1.getRange(2, 3, allMails.length, allMails[0].length).setValues(allMails);
debugger; // Pause in debugger
Browser.msgBox("Operation complete");
}
Option 2: Post-filter
Here's the alternate approach, removing duplicates after the array is built. The JavaScript magic here was lifted from this answer. We still use a one-dimensional array to collect and filter addresses. There's also an extra step required to remove our own address from the list.
Performance: This should be faster than approach 1, as there will be fewer comparisons required. HOWEVER, the bulk of the time used in the whole operation is tied up in accessing messages, so time savings in native JavaScript are negligible.
function getUniqueFromAddresses2() {
var my_email = Session.getActiveUser().getEmail();
var threads = GmailApp.getInboxThreads();
var n=threads.length;
var allMails = [];
for (var i=0; i<n; i++)
{
var thread = threads[i];
var messages=thread.getMessages();
for (var j=0; j<messages.length; j++)
{
var message=messages[j];
var getfrom = message.getFrom();
// Use RegEx to extract just email address
var email_address = getfrom.match(/[^<> ]*\#[^> ]*/)[0];
// Save the address
allMails.push(email_address);
// Skip messages I sent or addresses already collected
var index = allMails.indexOf(email_address);
if (email_address !== my_email && allMails.indexOf(email_address) == -1) {
allMails.push(email_address);
}
}
}
// Remove duplicates - https://stackoverflow.com/a/32533637/1677912
allMails = allMails.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, []);
// Remove my address
if ((mine=allMails.indexOf(my_email)) > -1) allMails.splice(mine,1);
// Could do further filtering & sorting of allEmails here
allMails = allMails.sort()
Logger.log(JSON.stringify(allMails));
// convert allMails array to two-dimensional array
allMails = allMails.map( function(item){ return [item]; });
Logger.log(JSON.stringify(allMails));
sheet1.getRange(2, 3, n, 1).setValues(allMails);
debugger; // Pause in debugger
Browser.msgBox("Operation complete");
}
How did you get the email addresses?
The original function took several steps to identify an email address in the string returned by message.getFrom(). It's tricky, because that string can contain just an email address, or a name and an address. The operation can be simplified by using a regular expression to match just the email address, and ignore whatever other text is in the string.
// Use RegEx to extract just email address
var email_address = getfrom.match(/[^<> ]*\#[^> ]*/)[0];
The expression looks for # and the text immediately before and after it, bordered by a space or angle braces. You can try this out in an online demo.
/[^<> ]*\#[^> ]*/
[^<> ]* match a single character not present in the list below
Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed [greedy]
<> a single character in the list "<> " literally (case sensitive)
\# matches the character # literally
You need to cross check your allMails array for a given email address to ensure it's not in the list, however you can't easily check against allMails directly because it is a two-dimensional array.
I would add a single dimensional array purely for the purpose of cross-checking.
var n=threads.length;
var messages=thread.getMessages();
var getfrom = 0;
var allMails = [];
var cross_check = [];
for (var i=0; i<n; i++)
{
for (var j=0; j<messages.length; j++)
{
var message=messages[j];
getfrom = message.getFrom();
var first_name = getfrom.substring(0, getfrom.indexOf(" "));
var last_name = getfrom.substring(getfrom.indexOf(" ")+1, getfrom.indexOf(" <"));
var email_address = 0;
if (first_name == '' && last_name == '')
{
email_address = getfrom;
} else {
email_address = getfrom.substring(getfrom.indexOf("<")+1, getfrom.indexOf(">"));
}
if(email_address != my_email && cross_check.indexOf(email_address) == -1){
cross_check.push(email_address);
allMails.push([email_address]);
}
}
}
Logger.log(allMails);
sheet1.getRange(2, 3, n, 1).setValues(allMails);
Browser.msgBox("Operation complete");
See the documentation for the indexOf function, which explains why we check against -1, here:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
Also check the Gmail Extractor - it saves the email addresses from Gmail in a Google spreadsheet.

ExtendScript get index number from searched Comp result

I've got this code that is sorting through the various items in after effects and returning all the compositions in the project, then I narrow it down based on the specific composition i'm looking for, in this case one ending with assemble. I get the name and that's great but what I really need is the index number to come along with the name, so that when I search for assemble I get a return of app.project.item(3), its index in the project window. Every time I try to get the number from the array all I seem to get is the total number of items which doesn't help.
Thanks.
function retrieveProjectItems(itemType){
var typeOptions = ["Composition", "Folder", "Footage"];
for(var t = 0; t<3; t++){
if(itemType == typeOptions[t]){
var proj, itemTotal, curItem, itemArray;
itemAry = [];
proj = app.project;
itemTotal = proj.numItems;
for(var i = 1; i <= itemTotal; i++){
curItem = proj.item(i);
//alert(curItem.name);
if(curItem.typeName == itemType){
itemAry[itemAry.length] = curItem.name;
}
}
return itemAry;
}
}
}
retrieveProjectItems("Composition");
//alert(comps); lists all COMPS in the Array
var comps = itemAry;
var compWithAssemble;
for(var i in comps){
if(comps[i].indexOf("assemble") > -1){ ///search for part of the name///////////////////////////////////
compWithAssemble = comps[i];
break;
}
}
// compWithAssemble has the string you are looking for.
alert(compWithAssemble);
//app.project.item(3).selected = true;
compWithAssemble.selected = true; //I'm looking to make this work...
I am assuming you want to programatically find the composition with a layer named "assemble"
This bit of code
if(comps[i].indexOf("assemble") > -1){ ///search for part of the name///////////////////////////////////
compWithAssemble = comps[i];
break;
}
does not give you the results you want because comps[i] is a object of CompItem, not an Array or a collection. You need to first retrieve the Layer Collection for each comp[i]. Then, when you have that LayerCollection, you can find the layer named "assemble" by using the .byName() method. If you don't get a returned layer, you'll receive null, otherwise, you'll receive a Layer Object.
It might look something like:
var comps = itemAry;
var compWithAssemble;
for (var i in comps){
if(comps[i].layers.byName("assemble") != null) {
compWithAssemble = comps[i];
break;
}
}

Loop over array and compare to other array

I'm trying to create a code that will take a sentence as a param, split that sentence into an array of words and then create a loop that checks if any of theses word matches a word in some other arrays.
In the example below, I have a sentence that contains the word "ski". This means that the return value should be categories.type3.
How can I have make the loop check this? Could I have a function switching between different categories ? (ie : if a word is not in action, look in adventure and so on).
var categories = {
type1: "action",
type2: "adventure",
type3: "sport"
}
var Sentence = "This sentence contains the word ski";
var sport = ["soccer", "tennis", "Ski"];
var action = ["weapon", "explosions"];
var adventure = ["puzzle", "exploring"];
var myFreeFunc = function (Sentence) {
for (var i = 0; i < arrayLength; i++) {
if (typeArr[i] == word) {
}
}
}
You appear to want to know which categories match the sentence.
To start with, get rid of the meaningless type1 etc identifiers and re-arrange your fixed data into objects that directly represent the required data, specifically a Map of key/value pairs, where each key is a "category" name, and each value is a Set of keywords associated with that category:
var categories = new Map([
['action', new Set(['weapon', 'explosions'])],
['adventure', new Set(['puzzle', 'exploring'])],
['sport', new Set(['soccer', 'tennis', 'ski'])]
]);
[NB: Set and Map are new ES6 features. Polyfills are available]
You now have the ability to iterate over the categories map to get the list of categories, and over the contents of each category to find the key words:
function getCategories(sentence) {
var result = new Set();
var words = new Set(sentence.toLowerCase().split(/\b/g)); /* "/b" for word boundary */
categories.forEach(function(wordset, category) {
wordset.forEach(function(word) {
if (words.has(word)) {
result.add(category);
}
});
});
return result.values(); // NB: Iterator interface
}
NB: I've avoided for .. of because it's not possible to polyfill that, whereas Set.prototype.forEach and Map.prototype.forEach can be.
I would rewrite the code (you should always combine var statements).
I've added a small fiddle snippet, how i would rewrite the function. Just as an example, how you could iterate your data. Of course you should check out the other posts to optimise this code snipped ( e.g. fix for multiple spaces! ).
// make sure, your dictionary contains lower case words
var categories = {
action: ["soccer", "tennis", "ski"],
adventure: ["weapon", "explosions"],
sport: ["puzzle", "exploring"]
}
var myFreeFunc = function myFreeFunc(Sentence) {
// iterates over all keys on the categories object
for (var key in categories) {
// convert the sentence to lower case and split it on spaces
var words = Sentence.toLowerCase().split(' ');
// iterates the positions of the words-array
for (var wordIdx in words)
{
// output debug infos
console.log('test:', words[wordIdx], categories[key], categories[key].indexOf(words[wordIdx]) != -1, '('+categories[key].indexOf(words[wordIdx])+')');
// lets the array function 'indexOf' check for the word on position wordIdx in the words-array
if (categories[key].indexOf(words[wordIdx]) != -1 ) {
// output the found key
console.log('found', key);
// return the found key and stop searching by leaving the function
return key;
}
}//-for words
}//-for categories
// nothing found while iterating categories with all words
return null;
}
stripped down the function part snippet (no comments, no extra spaces, no console.log):
var myFreeFunc = function myFreeFunc(Sentence) {
for (var key in categories) {
var words = Sentence.toLowerCase().split(' ');
for (var wordIdx in words)
{
if (categories[key].indexOf(words[wordIdx]) != -1 ) {
return key;
}
}
}
return null;
}
Accumulated the topics covered in the comments
check if the Object really owns the property: obj.hasOwnProperty(prop)
split string by word bounds, as mentioned by Alnitak (using RegExp): /\b/g
collecting categories for multiple matching
Snippet:
var myFreeFunc = function myFreeFunc(Sentence) {
var result = []; // collection of results.
for (var key in categories) {
if (categories.hasOwnProperty(key)) { // check if it really is an owned key
var words = Sentence.toLowerCase().split(/\b/g); // splitting on word bounds
for (var wordIdx in words)
{
if (categories[key].indexOf(words[wordIdx]) != -1 ) {
result.push(key);
}
}
}
}
return result;
}
One simple way would be to do like this :
function determineCategory(word){
var dictionnary = {
// I assume here you don't need category1 and such
action: ["weapon", "explosions"],
aventure: ["puzzle", "exploring"],
sport: ["soccer", "tennis", "ski"]
}
var categories = Object.keys(dictionnary);
for(var i = 0; i<categories.length; i++){
for(var j = 0; j<categories[i].length;j++){
var wordCompared = dictionnary[categories[i]][j];
if(wordCompared == word){
return categories[i];
}
}
}
return "not found";
}
var sentence = "This sentence contains the word ski";
var words = sentence.split(" "); // simple separation into words
var result = [];
for(var i=0; i<words.length; i++){
result[i] = determineCategory(words[i]);
}
A few notes on this approach :
it needs you to change your existing structure (I don't know if its possible)
it doesn't do much for your sentence splitting (just using the white space). For more clever approach, see Alnitak's answer, or look for tokenization/lemmatization methods.
it is up to you to determine what to do when a word doesn't belong to a category (right now, it just stores "not found".

Not able to compare variables in JQuery

I have an array of records. I want to search a string at the specific position of the array. But some how I am not able to do so. Kindly see the code below:
var match_index = [];
var count = 0;
var keyword1 = csvvalue[1][9].replace(/\"/g, '');
var search_text="इलाहाबाद";
$("#leng").html(csvvalue.length);
for(var i=0; i<csvvalue.length; i++){
$("#index").html("loop");
var keyword1 = csvvalue[i][9].replace(/\"/g, '');
if (search_text === keyword1)
{
match_index[count] = i;
count++;
$("#index").html("match");
}
$("#index").append("<br />" + i.toString());
}
In the above code, the control is is not going inside the if statement, though the string is available in the array at index 1 and 2. Also only the last value of i is getting printed (last line of the code) though it should print all the values of i starting from 0.
My actual requirement is to search through entire array for a specific string. I have changed the code to suit my requirement better.
Edited
I tried every thing but the control is not going inside the if statement though there are two matching records
You are comparing two values set before the loop
I guess it should be more like :
var match_index = [];
var count = 0;
var keyword1 = "";
var search_text="इलाहाबाद";
$("#leng").html(csvvalue.length);
for(var i=0; i<csvvalue.length; i++){
keyword1 = csvvalue[i].replace(/\"/g, '');
$("#index").html("loop");
if (search_text === keyword1)
{
match_index[count] = i;
count++;
$("#index").html("match");
}
$("#index").append("<br />" + i.toString());
}
Or depending on how your csvvalue array is structured.
keyword1 = csvvalue[1][i].replace(/\"/g, '');
Why loop through the whole array if you want to check a specific variable in the array.
You could just do something like
if (search_text === csvvalue[1][9].replace(/\"/g, '') {
//do something
}
Unless you really need to know how many times you run through the array.

Categories