Split an array when meets a certain number? - javascript

Hello I need to return count of chunks in given array of numbers.
Chunk can be defined assequence of one or more numbers separated by one or more zeroes.
Example: array [5, 4, 0, 0, -1, 0, 2, 0, 0] contains 3 chunks
so the answer should be 3 since the array can be split into three chunks.
Can you help me with the solution to this one?
Ive looped through the array but dont know how to deal with the multiple zeros.

In case you're interested in a good-performance, concise solution, in a single iteration...
Here's one based on iter-ops library:
import {pipe, count, filter, split} from 'iter-ops';
const data = [5, 4, 0, 0, -1, 0, 2, 0, 0];
const c = pipe(
data,
split(v => v === 0), // split into chunks, by zero
filter(a => !!a.length), // ignore empty chunks
count() // count resulting chunks
).first; // take the first value (our count)
console.log(c); //=> 3
P.S. I'm the author of the library.

You could add a flag for keeping track of counting the actual chunk and rest it only by fing a zero.
let data = [5, 4, 0, 0, -1, 0, 2, 0, 0],
count = 0,
lastCount = false;
for (const value of data) {
if (value === 0) {
lastCount = false;
continue;
}
if (!lastCount) {
count++;
lastCount = true;
}
}
console.log(count);

If you needed to keep track of the chunks as well you can loop it using a simple for loop like so:
var arr = [5, 4, 0, 0, -1, 0, 2, 0, 0];
var chunks = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] !== 0){ // check that the current number isn't a 0
if(arr[i - 1] === 0 || arr[i - 1] === undefined){ // check if previous number was a 0
chunks.push([]); // add a new chunk if previous number was a 0
}
chunks[chunks.length - 1].push(arr[i]); // push the number to latest chunk
}
}
//console.log(chunks);
console.log("There are " + chunks.length + " chunks");

A simple solution leveraging the second parameter of indexOf() within a while loop.
function count_chunks(arr, delimeter = 0) {
let count = 0, lastIndex = 0, index = 0;
while (index !== -1 && lastIndex < arr.length) {
index = arr.indexOf(delimeter, lastIndex);
if (index - lastIndex) {
count++;
}
lastIndex = index + 1;
}
return count;
}
const data = [5, 4, 0, 0, -1, 0, 2, 0, 0];
console.log(count_chunks(data));
// leading zero
console.log(count_chunks([0, 5, 4, 0, 0, -1, 0, 2]));
// no head or tail delimeters
console.log(count_chunks([5, 4, 0, 0, -1, 0, 2]));
// different delimeter
console.log(count_chunks([5, 4, 'x', 'x', -1, 'x', 2, 'x', 'x'], 'x'));

You can solve it by using Regular expressions.
let array = [5, 4, 0, 0, -1, 0, 2, 0, 0];
let arrayToString = array.join("").toString();
let occurrenceRegex = /0+/g; // Regex that will check for one or more occurrences
let result = arrayToString.match(occurrenceRegex).length;
console.log(result); // Output: 3

Related

For loop not iterating through whole array (Javascript)

I am trying to solve a problem that takes an array and moves all of the zeros to the end, preserving the order of the other elements.
This isn't iterating through the entire array, any ideas on what I'm doing wrong would be greatly appreciated.
let moveZeros = function (arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) {
newArr.push(arr[i]);
arr.splice(i, 1);
}
}
return arr.concat(newArr);
};
moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]);
// expexted to return [9,9,1,2,1,1,3,1,9,9,0,0,0,0,0,0,0,0,0,0]
// currently returns [9,9,1,2,1,1,3,1,9,0,9,0,0,0,0,0,0,0,0,0]
// Strangely... sending a smaller array to the function seems to work.
//moveZeros([false,1,0,1,2,0,1,3,"a"])
// correctly returns[false,1,1,2,1,3,"a",0,0]
The problem isn't related to the length, it occurs when you have more than one consecutive 0. For example [0,0,9,0,0,9] will come out [0,9,0,9,0,0].
In the first iteration of the loop (i=0), you remove the first 0 from the array, leaving you with [0,9,0,0,9]. On the second iteration (i=1), it's now looking at the second element in the array, which is 9. The first 0 is skipped. This will happen later in the array too as the loop progresses.
In general it can be problematic to modify an array as you loop through it. There are a number of ways you could perform a sort like this. But to keep it close to your original you could do this:
let moveZeros = function(arr) {
let arrA = [];
let arrB = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) {
arrB.push(arr[i]);
} else {
arrA.push(arr[i]);
}
}
return arrA.concat(arrB);
};
Now the function keeps the original array intact as it goes through it. Non-0 items are pushed to arrA, and 0s are pushed to arrB, and then those two are concatenated.
You could keep the array and move all not zero values to the start and fill the rest with zeroes.
let moveZeros = function(array) {
let i = 0,
j = 0;
while (i < array.length) {
if (array[i] !== 0) array[j++] = array[i];
i++;
}
while (j < array.length) array[j++] = 0;
return array;
};
console.log(...moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
console.log(...moveZeros([false, 1, 0, 1, 2, 0, 1, 3, "a"]));

Smallest unused ID in JavaScript

I'm trying this quiz on Codewars: Smallest unused ID
You've got much data to manage and of course you use zero-based and non-negative ID's to make each data item unique!
Therefore you need a method, which returns the smallest unused ID for your next new data item...
Note: The given array of used IDs may be unsorted. For test reasons there may be duplicate IDs, but you don't have to find or remove them!
In short, You are given an array. What the quiz requires is to find the smallest unused number.
For example:
[0,1,2,3,5] // Output: 4
[1,2,3,5] // Output: 0
[0,1,2,3,4,5] // Output: 6
[0,0,0,0,0,0] // Output: 1
[0,1,0,2,0,3] // Output: 4
My code can't pass some tests. Inputs for the tests are hidden, so I am unable to figure out the causes. What is causing the issue?
const nextId = ids => {
let lowestId;
if (Math.max(...ids) + 1 === ids.length) {
lowestId = Math.max(...ids) + 1;
} else {
let sortedIds = ids.sort((a, b) => a - b);
for (let i = 0; i < sortedIds.length; i++) {
if (i !== sortedIds[i]) {
lowestId = i;
break;
}
}
}
return lowestId
}
console.log(nextId([0, 1, 2, 3, 5])) // 4
console.log(nextId([1, 2, 3, 5])) // 0
console.log(nextId([0, 1, 2, 3, 4, 5])) // 6
console.log(nextId([0, 0, 0, 0, 0, 0])) // 1
console.log(nextId([0, 1, 0, 2, 0, 3])) // 4
You're over complicating it
function doStuff(stuff)
{
for(let i = 0; i < stuff.length + 1; i++)
{
if(stuff.indexOf(i) == -1)
return i;
}
}
It looks like you missed this line of the instructions:
"Note: The given array of used IDs may be unsorted. For test reasons there may be duplicate IDs, but you don't have to find or remove them!"
With the logic of your code, if there's a duplicate then the value won't match the index. You need to actually compare the values to account for duplicates.
function nextId(ids) {
let lowestId = 0;
let sortedIds = ids.sort((a, b) => a - b);
for (let i = 0; i < sortedIds.length; i++) {
if (lowestId === sortedIds[i] && lowestId !== sortedIds[i + 1]) {
lowestId++;
}
else if (lowestId !== sortedIds[i + 1]) {
return lowestId;
}
}
return lowestId
}
This approach takes an object for seen values.
The start node is -1, because the first missing id could be zero.
Performance:
To collect all nodes: O(n).
To find the missing smallest id: smaller than O(n).
function nextId(ids){
const seen = {};
let id = -1;
for (const id of ids) seen[id] = true;
while (seen[++id]) ;
return id;
}
console.log(nextId([0, 1, 2, 3, 5])); // 4
console.log(nextId([1, 2, 3, 5])); // 0
console.log(nextId([0, 1, 2, 3, 4, 5])); // 6
console.log(nextId([0, 0, 0, 0, 0, 0])); // 1
console.log(nextId([0, 1, 0, 2, 0, 3])); // 4

Function in Javascript that returns closest distance of two given array indices

I want to write a function with for-loops that finds the index of the number 1 in an array and returns the difference to the index of the number 2 that is closest to number 1 (number 1 only appears once). For instance:
Input: [1, 0, 0, 0, 2, 2, 2]
Output: 4
Input: [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2]
Output: 1
My try
function closest (array) {
let elem=array.findIndex(index=>index === 1)
let numberplus=0;
let numberminus=0;
for (let i=elem; i<array.length; i++){
if (array[elem+1] === 2)
{numberplus+=array[elem+1]-elem;}
break;
}
for (let i=elem; i>=0; i--) {
if (array[elem-1] ===2)
{numberminus+=array[elem-1]-elem;}
break;
}
if (numberplus < numberminus) {
return numberplus
} else {
return numberminus}
}
When invoked, the function just returns '0'. Thanks for reading!
Take the position of 1 as starting point and loop up and (if necessary) down the array:
const log = (arr, d) => console.log(`mimimal distance [${arr.join()}]: ${d}`);
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0, 2];
const arr2 = [1, 0, 0, 0, 2, 2, 2];
const arr3 = [2, 0, 1, 0, 2, 2, 2];
const arr4 = [2, 1, 0, 0, 2, 2, 2];
log(arr, clostes(arr));
log(arr2, clostes(arr2));
log(arr3, clostes(arr3));
log(arr4, clostes(arr4));
function clostes(arr) {
// determine position of 1
const indxOf1 = arr.indexOf(1);
// create array of distances
const distances = [0, 0];
// forward search
for (let i = indxOf1; i < arr.length; i += 1) {
if (arr[i] === 2) {
break;
}
distances[0] += arr[i] !== 2 ? 1 : 0;
}
// if 1 is # position 0 backwards search
// is not necessary and minimum equals the
// already found maximum
if (indxOf1 < 1) {
distances[1] = distances[0];
return Math.min.apply(null, distances);
}
// backwards search
for (let i = indxOf1; i >= 0; i -= 1) {
if (arr[i] === 2) {
break;
}
distances[1] += arr[i] !== 2 ? 1 : 0;
}
return Math.min.apply(null, distances);
}
Something like this will do the job. You could make the code shorter but I've tried to make it clear. Once we find 1, start at that index and keep checking adjacent indices. We also do bounds checking to ensure we don't overflow either end.
function closest(arr) {
const index = arr.findIndex(n => n === 1);
const len = arr.length;
let offset = 1;
while (true) {
const before = index - offset;
const after = index + offset;
const beforeBad = before < 0;
const afterBad = after >= len;
// It's necessary to check both, we could exceed the bounds on one side but not the other.
if (beforeBad && afterBad) {
break;
}
if ((!beforeBad && arr[before] === 2) || (!afterBad && arr[after] === 2)) {
return offset;
}
++offset;
}
return -1;
}
You could approach this using entries and reduce.
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2];
const goal = arr.indexOf(1);
const indices = [];
// Find all the indices of 2 in the array
for (let x of arr.entries()) {
if (x[1] === 2) indices.push(x[0]) ;
}
// Find the index that is closest to your goal
const nearestIndex = indices.reduce((prev, curr) => {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
}); // 5
console.log(Math.abs(goal - nearestIndex)); // 1
How about this:
Output = Input.map((cur,idx,arr)=>cur==2?Math.abs(idx-arr.indexOf(1)):Infinity).sort()[0]
You could avoid for loops here in favor of a more functional style. The function minDist takes m, n, and array as arguments, and returns the minimum distance between the first occurrence of m and any occurrence of n in an array.
First, map is used to create an array with pairs for each element containing the distance to the target m element and the value of the current element. Then filter is used to keep only the pairs representing n elements. Then sort is used so that the pairs representing the closest elements are at the beginning of the array. Finally, the [0] pair of the sorted array represents the closest element, and the [0] element of this closest pair is the minimum distance.
function minDist(m, n, array) {
let index = array.indexOf(m);
return array
.map((x, i) => [Math.abs(i - index), x])
.filter(p => p[1] === n)
.sort()[0][0];
}
console.log(minDist(1, 2, [1, 0, 0, 0, 2, 2, 2]));
console.log(minDist(1, 2, [2, 0, 0, 0, 2, 2, 1, 0, 0, 2]));

Move specified characters in an array

I get a list of elements given as parameter, this is input.
JSON.stringify(moveZeros([1,2,0,1,0,1,0,3,0,1]))
Script should move zeros to the end without changing other elements order.
My solution would looks like this code:
var moveZeros = function (arr) {
var args = Array.prototype.slice.call(arguments);
for(var i=0; i<args.length; i++) {
if (args[i] == 0)
args.splice(i);
args.push(0);
}
console.log(args);
return args;
}
This should append element's with zero value and append a new, 0 value to end of array. It only prints original array without modification.
It is because arguments is all the arguments passed in. You should be using arr and not arguments.
var moveZeros = function (arr) {
var args = arr;
...
}
Also another issue you will face is when you loop from the start to the end and you move elements to the end, you will be skipping indexes as stuff slides down to fill in the hole that you just created. You should be using reduce() or loop from the end to the start if you are using a for loop.
var moveZeros = function(arr) {
var args = arr;
for (var i = args.length - 1; i >= 0; i--) {
if (args[i] === 0) {
args.splice(i, 1);
args.push(0);
}
}
return args;
}
console.log(JSON.stringify(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1])));
You could use the copy of arr and use a variable for the length check. If a zero is found, the length variable is decremented and the zero is pushed to the end. If not found, the index is incremented.
var moveZeros = function (arr) {
var args = arr.slice(),
i = 0,
l = args.length;
while (i < l) {
if (args[i] === 0) {
args.push(args.splice(i, 1)[0]);
l--;
} else {
i++;
}
}
return args;
}
console.log(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1]));
Or use a loop from the end.
var moveZeros = function (arr) {
var args = arr.slice(),
i = args.length;
while (i--) {
if (args[i] === 0) {
args.push(args.splice(i, 1)[0]);
}
}
return args;
}
console.log(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1]));
You had some brackets wrong and missed some arguments. Also you are using an array as the parameter, no need to slice the arguments. You also missed the number of elements for the splice
This does not work for something like [9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0] (see update further below)
// DOES NOT WORK
var moveZeros = function (arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
arr.splice(i, 1);
arr.push(0);
}
}
console.log(arr);
return arr;
}
Working solution
var moveZeros = function(args) {
var r = [];
var zeros = 0;
for (var i = 0; i < args.length; i++) {
if (args[i] !== 0) {
r.push(args[i]);
} else zeros++
}
r = r.concat(Array(zeros).fill(0))
console.log(r);
return args;
}
JSON.stringify(moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
You have to provide count of values to be removed in splice() method.
Also push 0 only if the value was 0. You are pushing it everytime. (Hint: Because the push statement is not in if).
With a copy of array :
You can maintain a new array and push only non zzero values on to it and later push all the zeros. Simple to understand.
var moveZeros = function(args) {
var arr = [], zCount = 0;
for (var i = 0; i < args.length; i++) {
args[i] == 0 ? zCount++ : arr.push(args[i]);
}
while (zCount-- > 0) arr.push(0);
console.log(arr);
return arr;
}
JSON.stringify(moveZeros([9, 0, 0, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
With reverse loop :
You need a reverse loop because every time when you splice you skip an index so repeated 0s will not be removed.
var moveZeros = function(args) {
for (var i = args.length; i > 0; i--) {
if (args[i] == 0) {
args.splice(i, 1);
args.push(0);
}
}
console.log(args);
return args;
}
JSON.stringify(moveZeros([9, 0, 0, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
I think this would be the funniest way and nicest as line of code and as it's using built-in function (sort) then it faces less performance issues.
var array = [1, 2, 0, -1, 0, 1, 0, 3, 0, 1]
array.sort(a => a === 0);
console.log(array);
first its bad to modify an array while you iterate over it. this can cause some weird jumping that will be confusing.
say you have 1,0,0,1
in your loop when you get to index one it will splice and append the zero to the end of the array
1,0,1,0
but the counter will then increment to 2 and it will miss the 0 which is now at position 1.
It looks like youre trying to create a shallow copy with
var args = Array.prototype.slice.call(arguments);
but I am not really sure.
I wouldn't modify the array you want to return but rather create some temp array and loop through the input, if its 0 up some counter, if its not 0 push it to the holding array, then push on as many 0s as the counter tells you to and return the temp array

Javascript check on array's elements

How can i check an array if its elements are all 0's except one which is 1?
sample:
array = [0, 0, 0, 1, 0];
check(array); //returns the index where it is 1 which is 3
array = [0, 3, 0, 2, 0];
check(array); //returns -1
array = [0, 3, 1, 2, 0];
check(array); //returns -1 again if there are non zero aside from 1, it should be one 1 and others are all 0.
array = [0, 0, 1, 0, 1];
check(array); //returns -1 again, there should just be one element of 1
function check(a) {
var index = -1;
for (var i = 0; i < a.length; i++) {
if (a[i] == 1) {
if (index < 0) {
index = i;
} else {
return -1;
}
} else if (a[i] != 0) {
return -1;
}
}
return index;
}
array1 = [0, 0, 0, 1, 0];
check(array1); //returns 3
array2 = [0, 3, 0, 2, 0];
check(array2); //returns -1
You can use a couple of filters, to generate an array of invalid numbers (i.e. not 0 or 1), and then an array of ones - from the original array. In the end, you can check the lengths of these resultant arrays to see if your criteria is met.
var others = a.filter(function(_item) { return (_item !== 1 && _item !== 0); }),
ones = a.filter(function(_item) { return (_item === 1); });
if(others.length === 0 && ones.length === 1) {
// valid
}
If array elements are guaranteed to be non-negative , then you can sum all elements of array. If sum is anything other than 1, it is not your desired array.
You don't have to loop array elements to calculate sum of elements. Use new reduce function of JavaScript Array. Look it up on web.
Things can get complicated , if array elements can be negative as well.

Categories