How to get the position of the second occurrence? - javascript

Let's say I have this array:
var a = [1,2,99,3,4,99,5];
I would like to get the position of the second 99, something like:
a.indexOf(99, 2) // --> 5
However the second argument in indexOf just tells where to start the search. Is there any built in functions to make this? If not how would you do it?
Thanks!

There's only indexOf and lastIndexOf. You could loop over it:
var a = [1,2,99,3,4,99,5];
var matches = []
for (var i=0; i<a.length; i++){
if (a[i] == 99) {
matches.push(i)
}
}
console.log(matches); // [2, 5]
If you always want the second occurrence Jan's method is also good:
a.indexOf(99, a.indexOf(99) + 1)
The indexOf call on the right finds the first occurrence, +1 then limits the search to the elements that follow it.

There is no built in function, but you can easily create your own, by iteratively applying indexOf:
function indexOfOccurrence(haystack, needle, occurrence) {
var counter = 0;
var index = -1;
do {
index = haystack.indexOf(needle, index + 1);
}
while (index !== -1 && (++counter < occurrence));
return index;
}
// Usage
var index = indexOfOccurrence(a, 99, 2);
But Matt's solution might be more useful.

Related

Problems with finding unique values in regEx

I have a javascript regEx that is supposed to find all values with curly brackets around them eg {} and return a list of the unique values. I thought that it was working perfectly but I found that it doesn't work depending on the sequence of values.
For example: If the target document contains {lorem}{lorem}{ipsem}{ipsem} the script logs what's wanted [lorem, ipsem] but {lorem}{ipsem}{ipsem}{lorem} the script logs [lorem, ipsem,lorem]. What am I doing wrong!?
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var result = str.match(/{.*?}/g).map(function(val) {
return val.replace(/[\])}[{(]/g, "");
//return val.replace(/(^.*\[|\].*$)/g,'');
});
//The purpose of sort_unique is to find one of every value or string represented in an array
function sort_unique(arr) {
if (result.length === 0) return arr;
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i - 1] !== arr[i]) {
ret.push(arr[i]);
}
}
for (var index = 0; index < ret.length; index++) {
Logger.log(ret[index]);
}
return ret;
}
result = sort_unique(result);
Logger.log("Getting final result for front end....");
Logger.log(result);
return result;
}
I believe part of your problem is the sort method. If you replace
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
with
arr = arr.sort();
Then the function appears to work, at least on my side.
This will run in O(n log n) time. You can do better without sorting, if you store the values you've found so far in a map instead of an array. This would run in linear time.
(Also you'll want to replace if (result.length === 0) return arr; with if (arr.length === 0) return arr; just to make your sort_unique function completely independent of the surrounding function.)
The simplest method would be to use a Set. Store each of the regex matches in a set, then return Array.from(mySet).
var mySet = new Set();
str.match(/{.*?}/g).forEach(function(val) {
mySet.add(val.replace(/[\])}[{(]/g, ""));
});
return Array.from(mySet);
A set's add() function is O(1) so the total running time is O(n) where n is the number of matches in your string. Though, realistically, the regex search will be where most of the processing time occurs.
You check if the subsequent items are the same and those that are not subsequent land in the resulting array.
Check if the found value is in the result, and if not add the match, else, ignore.
Use the code like
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var m, result=[], rx = /{([^{}]*)}/g;
while (m=rx.exec(str)) {
if (result.indexOf(m[1]) == -1) {
result.push(m[1]);
}
}
result.sort(); // If you really want to sort use this
// Logger.log(result); // View the result
}
The /{([^{}]*)}/g regex matches {, then captures into Group 1 zero or more chars other than { and }. So, the value you need is in m[1]. The if (result.indexOf(m[1]) == -1) checks if the value is in result.

Find Longest Prefix That is Also a Suffix in String - Javascript

Link to codewars challenge
I need to return the length of the longest prefix that is also a suffix of a string in Javascript.
As far as I understand, the prefixes in "abcd" are:
['a', 'ab', 'abc']
And the suffixes in "abcd" are:
[ 'bcd', 'cd', 'd' ]
So the length of the longest prefix that is also a suffix in "abcd" in this case is 0, because there are no prefixes that are also suffixes in "abcd".
So far I've been able to figure out how to get the suffixes into an array for comparison, but not the prefixes.
function returnLongestPrefixAndSuffix(string) {
let prefixes = [];
let suffixes = [];
for (let i = 0; i < string.length -1; i++) {
prefixes.push(string.slice(i));
}
for (let i = 1; i < string.length; i++) {
suffixes.push(string.slice(i));
}
return prefixes + " " + suffixes;
}
console.log(returnLongestPrefixAndSuffix("abcd"));
I'm not grasping the concept of how to start at the beginning of a string and add a larger element to the array each time by one character, excluding the element that would include the last one.
Please follow my current logic if possible.
EDIT: My code now looks like this:
function solve(string) {
let prefixes = [];
let suffixes = [];
let includedList = [];
for (let i = 1; i < string.length; i++) {
prefixes.push(string.slice(0, i));
}
for (let i = 1; i < string.length; i++) {
suffixes.push(string.slice(-i));
}
console.log(prefixes);
console.log(suffixes);
for (let i = 0; i < prefixes.length; i++) {
let element = prefixes[i];
if (suffixes.includes(element) === true) {
includedList.push(element);
}
}
console.log(includedList);
if (includedList.length === 0) {
return 0;
}
else {
let overlap = prefixes.filter(value => suffixes.includes(value));
console.log(overlap);
let longest = includedList.sort(function (a, b) { return b.length - a.length; })[0];
return longest.length;
}
}
console.log(solve("abcdabc"));
And this is passing 10049 test but failing 163 tests on codewars. I still do not know what to do with the overlap variable or how to exclude overlaps from the includedList array.
function solve(string) {
for (let i = Math.floor(string.length / 2); i > 0; i--) {
let prefix = string.slice(0, i);
let suffix = string.slice(-i);
if (prefix == suffix) {
return i;
}
}
return 0;
}
console.log(solve("abcdabc"));
To account for the overlap, initialize your for-loop like this:
let i = Math.floor(string.length / 2)
That will initialize the for-loop at the half-way point in your string, so that you can count down and compare whether or not the prefix == the suffix, starting with the longest.
You could return prefix.length, but that will be the same thing as i.
Also, be sure to return 0 outside of the for-loop. Because if you try:
if (prefix != suffix) {
return 0;
}
inside of the for-loop, it will stop counting right there.
To get the prefixes, you can use the second argument of .slice:
string.slice(0, i)
Note that to get the suffixes, you could also take the string from the end:
string.slice(-i)
There is no sense in collecting prefixes and suffixes in arrays, just search for the biggest i where the suffix equals the prefix.
Please see the documentation of the slice function, it may take a second argument: https://www.w3schools.com/jsref/jsref_slice_string.asp
So following your logic, one way to get prefixes would be to:
for (let i = 1; i <= string.length; i++) {
prefixes.push(string.slice(0, i));
}
EDIT:
Your newest code doesn't work because of two reasons:
You may end up with includedList being empty, but you still try to get first element out of it.
You don't take overlaps into consideration. For the input aaa the correct result is a since prefix aa overlaps with the corresponding suffix. In other words the result can't be longer than half the length of the input.

Comparing values between two arrays

I'm trying to set up a function that checks if a word or a text is a palindrome. To do that, it splits the text so that every letter is an element of a new array, it takes rid of the white spaces and it makes the reverse array.
Then it checks if every element of the two arrays, at the same positions, are equal. If not it returns false, if yes it returns true.
Here the function:
function palindrome(str) {
var low = str.toLowerCase();
var newArray = low.split("");
var noSpace = newArray.filter(function(val) {
return val !== " ";
});
var reverse = noSpace.reverse();
function check (a, b) {
console.log(`checking '${a}' against '${b}'`);
var partial;
var result = 1;
for (var i = 0; i < a.length; i++) {
console.log(`comparing '${a[i]}' and '${b[i]}'`);
if (a[i] !== b[i]) {
result = 0;
} else {
partial = 1;
result *= partial;
}
}
return result;
}
var result = check(noSpace, reverse);
if (result == 1) {
return true;
} else {
return false;
}
}
palindrome("r y e");
I don't know what's wrong but it seems that the function keeps on returning a true value no matter what word or text I pass to the function. What is wrong with that?
Your issue seems to be because reverse() changes the actual array as well. So doing
var reverse = noSpace.reverse();
Will reverse noSpace and assign a reference to it on the variable reverse. That is, both arrays will be the same (reversed) array.
To bypass that, I've used .slice() to create a copy of the original array, and then called .reverse() on that new array, ridding you of any conflicts.
Here's a working snippet of what it looks like:
function palindrome(str) {
var str_array = str.toLowerCase().split("");
var no_space = str_array.filter(function(val) {
return val !== " ";
});
// By applying '.slice()', we create a new array
// reference which can then be reversed and assigned
// to the 'reverse' variable
var reverse = no_space.slice().reverse();
function check(a, b) {
var partial;
var result = 1;
for(var i=0; i < a.length; i++) {
if(a[i] !== b[i]) {
// We don't need to keep
// comparing the two, it
// already failed
return 0;
} else {
// I've kept this part even though
// I don't really know what it is
// intended for
partial = 1;
result *= partial;
}
}
return result;
}
return check(no_space, reverse) === 1;
}
console.log(palindrome("a b a"));
console.log(palindrome("r y e"));
The way you have coded for palindrome is way too complicated.
But there is one problem with your code: when you do a reverse() it changes the original array as well.
So you will need to make sure that you copy it via slice().
Also you can directly send a boolean result rather than doing a 1 and 0.
At result *= partial;, 1 * 1 will always equal 1
I didn't correct your code, but here is a optimized solution for you.
function palindrom(string) {
var arr = string.split("");
var lengthToCheck = Math.floor(arr.length / 2);
for (var i = 0; i < lengthToCheck; i++) {
if (arr[i] != arr[arr.length - (1 + i)]) {
return false;
}
}
return true;
}
First I split the array after every charater of the passed String. After that I get the half of the length of the array as it's enough to check just one half.
With the for-loop I compare the first half with the second half. As soon as I found two characters that do not match I return false. In case the whole first half matches the second half of the array, the for-loop will be completed and after that true will be returned.
What's actually happening is .reverse() reverses an array in place, it then stores a reference to that array which is not what you're calling in your check() method.
Simple fix would be to change your if statement:
if (a[i] !== b.reverse()[i])

Is this the right way to iterate through an array?

Here is the code in question:
var L1 = [];
var Q1 = [];
function populateListOne() {
var limit = prompt("please enter a number you would like to fill L1 to.");
for (i = 2; i <= limit; i++) {
L1[i] = i;
}
for (n = 2; n <= L1.length; n++) {
var count = 2;
if (n == count) {
var index = L1.indexOf(n);
L1.splice(index, 1);
Q1[n] = n;
count = count + 1;
}
for (j = 0; j <= L1.length; j++) {
if (L1[j] % 2 == 0) {
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
}
document.getElementById("demo").innerHTML = "iteration " + "1" + ": " + L1 + " Q1 = " + Q1;
}
I’m currently working on a homework assignment where I have to setup a queue. All is explained in my JSFiddle.
Problem description
Essentially, the part I’m stuck on is iterating through each instance of the array and then taking the value out if the modulus is identical to 0. However, as you can see when I run the program, it doesn’t work out that way. I know the problem is in the second for loop I just don’t see what I’m doing wrong.
The way I read it is, if j is less than the length of the array, increment. Then, if the value of the index of L1[j] modulus 2 is identical to 0, set the value of secondIndex to whatever the index of j is. Then splice it out. So, theoretically, only numbers divisible by two should be removed.
Input
A single number limit, which will be used to fill array L1.
L1 will be initialized with values 2, 3, ... limit.
Process
Get the starting element of array L1 and place it in array Q1.
Using that element, remove all values in array L1 that are divisible by that number.
Repeat until array L1 is empty.
You're going to have issues with looping over an array if you're changing the array within the loop. To help with this, I tend to iterate from back to front (also note: iterate from array.length - 1 as the length element does not exist, arrays are key'd from 0):
for(j = L1.length - 1; j >=0 ; j--)
For your first loop, you miss the elements L1[0] and L1[1], so I would change the first loop to:
L1 = [];
for(i = 2; i <= limit; i++)
{
L1.push(i);
}
In this section:
for(j = 0; j <= L1.length; j++){
if(L1[j] % 2 == 0)
{
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
you should splice with j instead of secondIndex.
Change L1.splice(secondIndex, 1); to L1.splice(j, 1);
Array indices and putting entries
You initial code used an array that was initialized to start at index 2. To avoid confusion, of what index to start at, start with index 0 and iterate until array.length instead of a predefined value limit to ensure that you go through each element.
The following still works but will be more of a headache because you need remember where to start and when you will end.
for (i = 2; i <= limit; i++) {
L1[i] = i; // 'i' will begin at two!
}
Here's a better way:
for (i = 2; i <= limit; i++) {
// 'i' starts at 2 and since L1 is an empty array,
// pushing elements into it will start index at 0!
L1.push(i);
}
Use pop and slice when getting values
When you need to take a peek at what value is at the start of your array, you can do so by using L1[0] if you followed my advice above regarding array keys.
However, when you are sure about needing to remove the starting element of the array, use Array.slice(idx, amt). idx specifies which index to start at, and amt specifies how many elements to remove beginning at that index (inclusive).
// Go to 1st element in L1. Remove (1 element at index 0) from L1.
var current = L1.splice(0, 1);
Use the appropriate loops
To make your life easier, use the appropriate loops when necessary. For loops are used when you know exactly how many times you will iterate. Use while loops when you are expecting an event.
In your case, 'repeat until L1 is empty' directly translates to:
do {
// divisibility checking
} while (L1.length > 0);
JSFiddle
Here's a complete JS fiddle with in-line comments that does exactly what you said.

Loop check if on last iteration?

How do I check if I'm on the last iteration of this loop? I'm sorry for asking this question. I'm used to programming in VB.NET and javascript seems very cryptic by nature.
if (QuerySplit.length > 1) {
var NewQuery
for (i=0; i<QuerySplit.length; i++)
{
// if we're not on the last iteration then
if (i != QuerySplit.length) {
// build the new query
NewQuery = QuerySplit[i].value + " AND "
}
}
}
Your i is always smaller than QuerySplit.length - that's your loop condition. In the last iteration it will have a value of QuerySplit.length-1, that's what you can check against:
if (i < QuerySplit.length - 1)
Btw, you'd do better to use the join Array method for what you're trying to do:
var NewQuery = QuerySplit.map(function(x){return x.value;}).join(" AND ");
Take note that you need var NewQuery = ""; and check for length - 1. Also, the last if statement is just a guess of what you probably want to do:
if (QuerySplit.length > 1) {
var NewQuery = "";
for (i = 0; i < QuerySplit.length; i++) {
// if we're not on the last iteration then
if (i != QuerySplit.length - 1) {
// build the new query
NewQuery += QuerySplit[i].value + " AND "
} else {
NewQuery += QuerySplit[i].value;
}
}
}
If QuerySplit.length is 4, then:
0, 1, 2, 3
...are the indexes. So you want to check for when the index is 3 and that's your last iteration.
The array is 0-based. This means if there are 3 items in the array, your indexes will be 0,1,2. The last one is one less than the length.
You'll have to check like this: (i < QuerySplit.length -1)

Categories

Resources