Find index of last matching occurrence - javascript

I've got array of variables and next variable which I want to add alphabetically. It goes A-Z and after that AA, AB, AC etc..
so in when next variable is E I want to add it at the end of letters with length=1, if next variable would be AC I'd add it at the end on letters with length=2 etc. I tried to do it with findIndex, but it returns the first occurrence, not the last one and lastIndexOf accepts value while in my case it should be the last element with given length.
let variables = ['A', 'B', 'C', 'D', 'AA', 'AB'];
const nextVariable = 'E';
const idx = variables.findIndex(x => x.length === nextVariable.length);
variables.splice(idx, 0, nextVariable);
console.log(variables);
// should be ['A', 'B', 'C', 'D', 'E', 'AA', 'AB']

You can just look for the first variable which is longer than the variable to insert, and if it doesn't exist (findIndex returns -1), add to the end of the array:
let variables = ['A', 'B', 'C', 'D', 'AA', 'AB'];
let nextVariable = 'E';
let idx = variables.findIndex(x => x.length > nextVariable.length);
variables.splice(idx < 0 ? variables.length : idx, 0, nextVariable);
// should be ['A', 'B', 'C', 'D', 'E', 'AA', 'AB']
console.log(variables);
nextVariable = 'AC';
idx = variables.findIndex(x => x.length > nextVariable.length);
variables.splice(idx < 0 ? variables.length : idx, 0, nextVariable);
// should be ['A', 'B', 'C', 'D', 'E', 'AA', 'AB', 'AC']
console.log(variables);

let variables = ['A', 'B', 'C', 'D', 'AA', 'AB'];
const nextVariable = 'E';
variables[variables.length] = nextVariable
variables = variables.sort((x,y) => x.length<y.length ? -1 : x.length==y.length ? x.localeCompare(y) : 1)
console.log(variables);

You can use a custom sort function and test the alphabetical order and length of each value.
function mySort(a, b) {
if(a.length == b.length) {
return a.localeCompare(b);
} else {
return a.length - b.length;
}
}
The you can use this function to sort the array after a new value has been added:
variables.sort(mySort);

Related

Group every two items in an array while sharing the endings, like a chain

Assuming I have an array like this: [A, B, C, D, E, F]
, How can I group them like this:
[[A, B], [B, C], [C, D], [D, E], [E, F]]
(Notice how every last element is shared with the next group, but with the opposite index.)
I know this ain't a big deal of a problem, but I'm trying to keep it simple and short, maybe with Array.reduce() if possible:
arr.reduce(function (rows, key, index) {
return (index % 2 == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
// Output: [[A, B], [C, D], [E, F]]
One liner solution is
arr.map((c, i) => [c, arr[i + 1]]).slice(0, -1)
SOLUTION 1
You can use map and filter here to achieve the result
At current index return an array which will contain current element and next element till last element
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = arr
.map((c, i) => (i < arr.length - 1 ? [c, arr[i + 1]] : null))
.filter(Boolean);
console.log(result);
SOLUTION 2
You can also acheve this if you get all array combination and remove last one as:
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = arr.map((c, i) => [c, arr[i + 1]]).slice(0, -1);
console.log(result);
Just with reduce method, you can add the current item and the item after it in an array, and then push this array into the accumulator of the reducer method, and before push you need to check if the current item isn't last item in the array.
const arr = ['A', 'B', 'C', 'D', 'E', 'F']
const result = arr.reduce((acc, item, index) => {
const nextItem = arr[index + 1]
nextItem ?? acc.push([item, nextItem])
return acc
}, [])
console.log(result)
If you don't want to stick to the reduce() approach here's another method using map() and slice().
const data = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = data.map((_, i) => (i < data.length - 1 ? data.slice(i, i + 2) : null)).filter(Boolean);
console.log(result);
The simplest will be a standard for loop. It happens to also be shorter than many of the reduce() answers with no extraneous filters or conditions.
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
const result = [];
for (let i = 0; i < arr.length - 1; i++) {
result.push([arr[i], arr[i + 1]]);
}
console.log(result);
Alternatively a while loop is actually shorter if a little less transparent.
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
let result = [], i = 0;
while (i < arr.length - 1) {
result.push([arr[i], arr[++i]]);
}
console.log(result);

Javascript count the duplicate array values until it changes

Sorry if this is a duplicate or a dumb question!
Basically, I need to get the count for duplicate values in an array until the next value changes. I can't use reduce() in my project, so any plain JS would be helpful.
let array = [a,a,a,b,b,b,b,b,c,c,c,a,d,d];
Results:
a:3,
b:5,
c:3,
a:1,
d:2
I would appreciate it very much.
You can use regex to get the desired result.
/([a-z])\1*/gi
let array = ["a", "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "a", "d", "d"];
const result = array
.join("")
.match(/([a-z])\1*/gi)
.map((s) => `${s[0]}${s.length}`);
console.log(result);
Simply loop over chars and check if char in dict then increment it else set it to 1;
let chars =  ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'a', 'd', 'd'];
const dic={}
for(char of chars){
if(char in dic){
dic[char]++;
}else{
dic[char]=1;
}
}
console.log(dic);//{a: 4, b: 5, c: 3, d: 2}
Run iteration over array elements. Find the next non-equal current character's position. then difference the two indexes you will find the current character's continuous last position. Increase the iteration so you need not worry about multiple counts. If no non-equal character found, then the size of the sub-array is the rest of the main array size.
let ara = ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'a', 'd', 'd'];
let index = 0;
for (var i = 0; i < ara.length; i++) {
let char = ara[i];
let size = 0;
let nextIndex = ara.findIndex(
(a, index) => a !== char && index > i);
if (nextIndex < 0) {
size = ara.length - i;
i = ara.length - 1;
} else {
size = nextIndex - i;
i = nextIndex - 1;
}
console.log(char + ' ' + size);

Pushing spliced items returns nested array

I am trying to push 3 random items from an array into a new array. I use splice() to grab an item and remove it from the old array. When I push() the items, I would like to get them back in a single array.
For example:
["b", "f", "a"]
This is my code:
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
let newletters = [];
for (let i = 0; i < 3; i++) {
newletters.push(letters.splice(Math.floor(Math.random() * letters.length), 1));
}
console.log(newletters);
It seems like I am pushing the spliced items as arrays into the new array, is there a way to fix this?
[
[
"b"
],
[
"f"
],
[
"a"
]
]
You could spread ... the array from Array#splice.
Or take the first element with and index.
letters.splice(Math.floor(Math.random() * letters.length)[0]
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
let newletters = [];
for (let i = 0; i < 3; i++) {
newletters.push(...letters.splice(Math.floor(Math.random() * letters.length), 1));
}
console.log(newletters);
You can use the function Array.prototype.concat instead.
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
let newletters = [];
for (let i = 0; i < 3; i++) {
newletters = newletters.concat(letters.splice(Math.floor(Math.random() * letters.length), 1));
}
console.log(newletters);
Use the ... operator inside the push statement.
const letters = ['a', 'b', 'c', 'd', 'e'];
const newLetters = []
newLetters.push(...letters.splice(1, 2)) // Equal to newLetters.push('b', 'c')
console.log(newletters)
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
let newletters = [];
for (let i = 0; i < 3; i++) {
newletters.push(letters.splice(Math.floor(Math.random() * letters.length), 1));
}
console.log(newletters.flat());

How to validate if a letter in an array is repeated?

I want to validate that a string within an array is not repeated more than 3 times, that is:
let array = ['A', 'A', 'A', 'B']
let array2 = ['A', 'A', 'A', 'A', 'B'] <-- Not valid
That the code does not continue to work, if the array it receives has values that are repeated those times
Thank you
You can use array.some() in combination with array.filter() to check if a value only exists an x amount of times.
const array = ['A', 'A', 'A', 'B'];
const array2 = ['A', 'A', 'A', 'A', 'B'];
const isValid = (arr, limit) => {
return !arr.some((char) => (
arr.filter((ch) => ch === char).length > limit
// use the next line for a case insensitive check
// arr.filter((ch) => ch.toLowerCase() === char.toLowerCase()).length > limit
));
}
console.log(isValid(array, 3));
console.log(isValid(array2, 3));
You could take a closure over the count of the last string and check the count or reset the count to one.
const
check = array => array.every(
(c => (v, i, { [i - 1]: l }) => l === v ? c++ < 3 : (c = 1))
(0)
);
console.log(check(['A', 'A', 'A', 'B']));
console.log(check(['A', 'A', 'A', 'A', 'B']));
You can count all the letters using reduce and then check those, like so:
let array = ['A', 'A', 'A', 'B'];
let array2 = ['A', 'A', 'A', 'A', 'B'];
const allElementsExistUpToN = (arr, n) => {
const counts = arr.reduce((acc, el) => {
acc[el] = acc[el] == undefined ? 1 : acc[el] +1;
return acc;
}, {});
return !Object.values(counts).some(c => c > n);
}
console.log(allElementsExistUpToN(array, 3));
console.log(allElementsExistUpToN(array2, 3));

Fastest way to check if array contains 2 different values?

Consider the following arrays:
['a', 'b', 'a'] //method should return true
['a', 'b', 'c'] //method should return true
['a', 'c', 'c'] //method should return false
I want to write a method that most efficiently checks to see if both 'a' and 'b' exist in the array. I know I can do this in a simple for loop
let a_counter = 0;
let b_counter = 0;
for (let i = 0; i < array.length; i++) {
if (array[i] === 'a') {
a_counter++;
}
if (array[i] === 'b') {
b_counter++;
}
}
return (a_counter > 0 && b_counter > 0);
But this isn't very short. I can do indexOf but that will loop through twice. I have also considered using a set as below:
const letter_set = new Set(array)
return (letter_set.has('a') && letter_set.has('b'))
But I am pretty unfamiliar with sets and don't know if this solution could potentially be more expensive than just looping. I know that has() operations should be faster than array iterations but constructing the set probably takes at least O(N) time (I'm assuming).
Is there a clean and efficient way to find multiple elements in an array? ES6 answers welcome
You can use every and includes to do this check.
So we are saying every item must be included in the array.
function contains(arr, ...items) {
return items.every(i => arr.includes(i))
}
console.log(contains(['a', 'b', 'a'], 'a', 'b'))
console.log(contains(['a', 'c', 'c'], 'a', 'b'))
console.log(contains(['a', 'b', 'c'], 'a', 'b', 'c'))
console.log(contains(['a', 'b', 'c', 'd'], 'a', 'b', 'c', 'd', 'e'))
You could use just the Set and check if the wanted items are in the items array.
const
check = (items, wanted) => wanted.every(Set.prototype.has, new Set(items));
console.log(check(['a', 'b', 'a'], ['a', 'b'])); // true
console.log(check(['a', 'b', 'c'], ['a', 'b'])); // true
console.log(check(['a', 'c', 'c'], ['a', 'b'])); // false
array.includes('a') && array.includes('b')
includes seems like a real handy way to check for specific elements, even if there is more than one.
Not as compact as the other examples, but it does do the job in single run.
const arr1 = ['a', 'b', 'a']; //method should return true
const arr2 = ['a', 'c', 'c']; //method should return false
const arr3 = ['a', 'b', 'c']; //method should return true
const reducer = ({ a, b }, char) => ({
a: a || char === 'a',
b: b || char === 'b'
});
const includesAnB = arr => {
const { a, b } = arr.reduce(reducer, {});
return a && b;
}
console.log(includesAnB(arr1));
console.log(includesAnB(arr2));
console.log(includesAnB(arr3));

Categories