for loop skipping one item in array in javascript - javascript

I am trying to make a function that removes strings from an array if they include a character in a certain other list
Here is the code:
var possible = ["salutations", "goodbye", "thanks", "welcome"];
var incorrect = ["o"];
console.log(possible);
function narrowdown(possible, incorrect)
{
var templist = possible;
for (i in possible)
{
console.log(i + " " + possible[i]);
var array1 = possible[i].split("");
var common = array1.filter(value => incorrect.includes(value));
console.log(common)
if (common.length)
{
templist.splice(i, 1);
}
}
possible = templist;
}
narrowdown(possible, incorrect);
console.log(possible);
Here I am trying to remove all the words that include the letter o. I created a temporary array in the function because it has happened to me before that a for loop skips items altogether.
The code first logs the index of the item in the list and then the item itself.
Then it turns the word into an array and checks for overlap between it and the "incorrect" array. It does that correctly and logs the overlapping characters.
The issue seems to be that it skips over the "goodbye" item for some reason. It doesn't even process it.
Here is the output I am getting:
[ 'salutations', 'goodbye', 'thanks', 'welcome' ]
0 salutations
[ 'o' ]
1 thanks
[]
2 welcome
[ 'o' ]
[ 'goodbye', 'thanks' ]

First of all, for (i in possible) is a bad way of looping through an array since it retrieves the keys before the loop begins and it never updates that list of keys. Also, if someone assigns an attribute to the array, like possible.foo = 17, then your loop will also go through that. The issue you're having is that when you splice the array, everything else is shifted one to the left, changing their indices to be one less, so your new index actually skips over the next element. The fix is to use a conventional for loop and decrement i after splicing:
for (let i = 0; i < possible.length; i ++) {
// more code...
if (common.length) {
templist.splice(i, 1);
i --;
}
}

The problem is that the array is re-indexed when you splice. One solution is to iterate in reverse:
var possible = ["salutations", "goodbye", "thanks", "welcome"];
var incorrect = ["o"];
console.log(possible);
function narrowdown(possible, incorrect)
{
var templist = possible;
var i = possible.length;
while (i--)
{
console.log(i + " " + possible[i]);
var array1 = possible[i].split("");
var common = array1.filter(value => incorrect.includes(value));
console.log(common)
if (common.length)
{
templist.splice(i, 1);
}
}
possible = templist;
}
narrowdown(possible, incorrect);
console.log(possible);

The issue comes from the line tempList = possible which is an assignment by reference, meaning that when you do the splice operation you do it on both arrays at the same time.
Generally it is considered bad form to manipulate data like that anyway, you should have narrowDown return a value that you re-assign to possible instead of filtering them in place. If you do that you can also leverage some of the newer array methods:
var possible = ["salutations", "goodbye", "thanks", "welcome"];
var incorrect = ["o"];
function narrowdown(possible, incorrect)
{
return possible.filter(item => !incorrect.some(test => item.includes(test)))
}
possible = narrowdown(possible, incorrect);
console.log(possible);
using the .some will also exit early as soon as there is a match, instead of looping over all incorrect values and the entire string, boosting performance slightly

If you're looking for a less "imperative" version of this function, you can make use of Array.filter(), Array.some(), and String.includes() to make a one-liner function that does the trick (for formatting purposes, I split it in the multiple lines in the snippet below).
const possible = ['salutations', 'goodbye', 'thanks', 'welcome'];
function narrowDown(possible, incorrect) {
return possible.filter((value) => (
!incorrect.some((exclude) => (value.includes(exclude)))
));
}
console.log('["o"]', JSON.stringify(narrowDown(possible, ['o'])));
console.log('["a"]', JSON.stringify(narrowDown(possible, ['a'])));
console.log('["a", "o"]', JSON.stringify(narrowDown(possible, ['a', 'o'])));

Related

Javascript - find all possible strings

Looking for some tips to solve this problem. We have string chain
const str = "543163431154",
and array with numbers where elements are basically numbers taken from this string
const array = ["21154", "543123", "163421154"]
What I'm looking for is to find all possible strings
Was thinking to split our input string as first, then create variable to store single letters. Subsequently I could iterate over newly created array in some kind of reduce function, but tbh I have no right solution
Here's an algorithm,
From your array, create 5 arrays, each one with different order of the elements from the original array
Iterate over each array
For each element, check using regex if the str starts with it (^currentElement)
If it is, remove from your str the current element and continue to the next element
If it's not, continue to the next array from the 5 you created
If you reach the last index of every array and str starts with it, add the current array to the results.
Here you go, now because it's not a code writing site and it's not a specific code question I wont write it for you - but the above algorithm will work for you.
Edit: After a bit more reading I understood the problem a bit better, and boy was this fun!
const str = "14316342115414321154"
const array = ["21154", "143", "21154143", "1634", "163421154"]
const findAllCombinations = (str, match) => {
const result = match.reduce((acc, layer) => {
if(str.indexOf(layer) === 0) {
if(str.substring(layer.length).length > 0) {
const nextLayer = findAllCombinations(str.substring(layer.length), match).map(c => addToLayer(layer, c))
acc.push(...nextLayer)
} else {
acc.push(addToLayer(layer, ""))
}
}
return acc;
}, [])
return result
}
const addToLayer = (layer, add) => `:${layer}${add}`
console.log(findAllCombinations(str,array))

Find word in string using RegEx javascript

How can I make a search function using regEx?
I have some code but can't post it at the moment due to my computer being very broken and annoying, I will try again tomorrow!
This is not really what would be called combinations, but permutations.
The idea is to use recursion for getting the result for a shorter array, i.e. the one that lacks the first element.
Then take all permutations you get back from the recursive call, and insert the left-out value in each possible index.
When input has duplicates, then you need to stop inserting the left-out value as soon as you find that same value preceding the insertion spot.
Here is how that looks:
function scramble(array) {
if (array.length == 0) {
return [[]];
}
let results = [];
// solve the problem for a shorter array (first value excluded), and iterate:
for (let perm of scramble(array.slice(1))) {
// add the missing element in each possible position:
for (let i = 0; i < array.length; i++) {
// next IF is only needed when input has duplicates, and
// output should not repeat same array:
if (i && array[0] === perm[i-1]) break;
results.push(perm.slice(0, i).concat(array[0], perm.slice(i)));
}
}
return results;
}
let array = ["I", "am", "coding", "am"];
console.log(scramble(array));
Without the inner if, an input with duplicate values will produce duplicate arrays. If this is not desired, then the if is needed.
You could iterate and get a flat array of the mapping of the value with the result of the nested calls.
function permutation(array) {
return array.length === 1
? [array]
: array.flatMap((v, i) => permutation([
...array.slice(0, i),
...array.slice(i + 1)
]).map(a => [v, ...a]));
}
permutation(["I", "am", "coding"]).map(a => console.log(...a));

Splitting an array of numbers and non-numbers into two separate arrays

I'm very new to javascript and I'm trying to create a function that takes a mixed input of numbers and then loops through the array in order to sort them into two newer arrays so I can manipulate them further.
So if I have an input of
1,a,2,b,3,c,4,d,
I want to make two new arrays of
1,2,3,4 and a,b,c,d
So far I've been able to split the input string at the comma, so that I now have
1a2b3c4d
all I need to be able to do now is just separate the numbers and non-numbers. I was thinking of using two loops: one that checks
if(isNan.array[n] == True )
and then trying to find a way to store the letters into a variable using the loop and then using another loop to do so for the numbers using another if function.
Is this how I should do it, and do you have any advice on how to go about it?
Edit:
I now have this code to check for letters:
if (isNaN(splitResult[L])) {
for (i = 0; i < splitResult; i++) {
letterArray.add(splitResult[L]);
L = L + 1
When I try to output the results to a box to count letters (using the variable L), nothing shows up. I doubt I've made a new array
just for completion, split the string into array first :)
let yourString = '1a2b3c4d';
let yourArray = yourString.split('');
let letterArray = [];
let numberArray = [];
yourArray.forEach(item => {
if(isNaN(item) && typeof item === 'string'){
letterArray.push(item);
}
else if(!isNaN(item) {
numberArray.push(item);
}
});
console.log(letterArray, numberArray);
All you need to do is loop through the array, you can use the Array prototypes forEach. Or you can use the normal for loop to check through each element of the array. You can now check if each element isNaN and then push into the right array appropriately. You can use the snippet below
const yourArray = ['1','a','2','b','3','c','4','d'];
const letterArray = [];
const numberArray = [];
yourArray.forEach((eachItem) => {
if(isNaN(eachItem)){
letterArray.push(eachItem);
} else {
numberArray.push(eachItem);
}
});
console.log(letterArray, numberArray);

Looping through an array with conditions

I'm having a tough time figuring out how to loop through an array and if certain items do exist within the array, i'd like to perform a .slice(0, 16) to kind of filter an already existing array (lets call that existing array "routes").
For example, a previous process will yield the following array:
points = ['=00ECY20WA200_RECV_P1SEL',
'=00ECY20WA200_RECV_P2SEL',
'=00RECV_C1A_EINCSCMPP1',
'=00RECV_C1A_EINCSCMPP2',
'=00BYPS_C1A_EINCSCMP',
'=00ECY20WA200_BYPS_SPSL1',
'=00ECC92AG184YB01',
'=00ECC92AG185YB01',
'=00ECC92AG186YB01',
'=00ECC92AG187YB01',
]
So if any of the above items exist in the "points" Array, which in this case they all do (but in some cases it could just be 1 of the 10 items existing there), I'm trying to perform routes.slice(0, 16) to the other already existing array.
I've tried lots of different ways (for loops with if statements) and at this point I'm not sure if its my syntax or what, but I'm back at square 0 and I don't even have a competent piece of code to show for. Any direction would be greatly appreciated.
You could use a hash table for checking and filtering.
var points = ['=00ECY20WA200_RECV_P1SEL', '=00ECY20WA200_RECV_P2SEL', '=00RECV_C1A_EINCSCMPP1', '=00RECV_C1A_EINCSCMPP2', '=00BYPS_C1A_EINCSCMP', '=00ECY20WA200_BYPS_SPSL1', '=00ECC92AG184YB01', '=00ECC92AG185YB01', '=00ECC92AG186YB01', '=00ECC92AG187YB01'],
hash = Object.create(null),
filtered = points.filter(function (a) {
if (!hash[a.slice(0, 16)]) {
hash[a.slice(0, 16)] = true;
return true;
}
});
console.log(filtered);
ES6 with Set
var points = ['=00ECY20WA200_RECV_P1SEL', '=00ECY20WA200_RECV_P2SEL', '=00RECV_C1A_EINCSCMPP1', '=00RECV_C1A_EINCSCMPP2', '=00BYPS_C1A_EINCSCMP', '=00ECY20WA200_BYPS_SPSL1', '=00ECC92AG184YB01', '=00ECC92AG185YB01', '=00ECC92AG186YB01', '=00ECC92AG187YB01'],
pSet = new Set,
filtered = points.filter(a => !pSet.has(a.slice(0, 16)) && pSet.add(a.slice(0, 16)));
console.log(filtered);
EDIT: So it seems like you want to remove an element from an array called routes for each element in the points array. This is how you could do this:
function removeBrokenRoutes(brokenPoints, routes){
for(let pt of brokenPoints){
let index = routes.indexOf(pt);
if(index !== -1) routes.splice(index,1);
}
return routes;
}
Keep in mind that the larger the arrays, the more time this is going to take to complete.
You could use the filter and indexOf methods in combination:
var arr = [/* all the data you're checking against */];
var points = [/* the data you're checking for */];
var filteredArr = arr.filter(function(x) {
// will return -1 if the point is not found
return points.indexOf(x) !== -1;
});
filteredArr will contain all the points that appear in both arrays. The filter function works by taking a function with one argument x, which represents each item in the array. if the function returns true, the item will be added to the new array (filteredArr), and if false the function will move on to the next item. indexOf will check if the item is found in the other array. Also it is important to note that you will need a more complex solution (such as a hashtable) if the data set is very, very large as this is not necessarily the most performant method. But it's a good place to start as it is easy to understand.

all possible combinations in javascript - why pop()?

I'm trying to understand this function that I came across online that returns all possible combinations of a string -- why exactly is the pop() call needed with nextLetter.pop? I tried debugging this in the console but it's not clear what the pop does exactly.
result with nextLetter.pop() ---> [ 'bac', 'bca', 'cba', 'cab', 'acb', 'abc' ]
result without nextLetter.pop() --> [ 'bac',
'bacca',
'baccacba',
'baccacbaab',
'baccacbaabacb',
'baccacbaabacbbc' ]
function stringPermutations(str) {
var permutations = [];
var nextLetter = [];
var chars = str.split('');
permutateInner(chars);
function permutateInner(chars) {
if (chars.length === 0) {
permutations.push(nextLetter.join(''));
}
for (var i = 0; i < chars.length; i++) {
chars.push(chars.shift());
nextLetter.push(chars[0]);
permutateInner(chars.slice(1));
//what is this doing?
nextLetter.pop();
}
}
return permutations;
}
console.log(stringPermutations('abc'));
permutateInner(chars.slice(1)); does recursion on array subset [1..n]. It is included to have permutation also for each subarray of current array.
e.g. when current first letter is b, recursion includes permutations ac and ca. when current first letter is a, recursion includes permutations cb and bc.
It is crucial to have it there, otherwise you would get only result [ 'abc', 'bca', 'cba' ]
Starting at the top, it's clear that nextLetter is an Array -- so the first place I'd look would be the docs on MDN.
As the documentation says, the .pop() method removes the last element from the Array and returns it -- in your sample code the return value isn't being used, so clearly what's useful is the side effect of removing the last element.

Categories

Resources