Array splitting based on value - javascript

How could I split an array by value like this:
[0, 1, 2, 0, 0, 0, 1, 0] =>
[[0, 1, 2], [0], [0], [0, 1], [0]]?
I'm using lodash documentary, but kinda out of ideas for now. Is there any way to do this with _.groupBy?
Thanks for your answers.

Use native JavaScrip Array#reduce method.
var data = [0, 1, 2, 0, 0, 0, 1, 0],
last;
var res = data.reduce(function(arr, v) {
// check the difference between last value and current
// value is 1
if (v - last == 1)
// if 1 then push the value into the last array element
arr[arr.length - 1].push(v)
else
// else push it as a new array element
arr.push([v]);
// update the last element value
last = v;
// return the array refernece
return arr;
// set initial value as empty array
}, [])
console.log(res);

Below is succinct solution in ES2015 (formerly ES6).
const newArray = [];
[0, 1, 2, 0, 0, 0, 1, 0].forEach(item => item === 0 ?
newArray.push([0]) :
newArray[newArray.length - 1].push(item)
);
console.log(newArray);

If you have to start a new array when you encounter a zero you can resort to
this code, hope it is not appear as vodoo programming.
var x = [0, 1, 2, 0, 0, 0, 1, 0];
x.join("") /* convert the array to a string */
.match(/(0[^0]*)/g) /* use a regex to split the string
into sequences starting with a zero
and running until you encounter
another zero */
.map(x=>x.split("")) /* get back the array splitting the
string chars one by one */
I assume that the array elements are just a digit long and that 0 is the start of every sub array.
Removing the one digit assumption would resort to this code:
var x = [0, 1, 2, 0, 12, 0, 0, 1, 0];
var the_regexp = /0(,[1-9]\d*)*/g;
x.join(",") /* convert the array to a comma separated
string */
.match(the_regexp) /* this regex is slightly different from
the previous one (see the note) */
.map(x=>x.split(",")) /* recreate the array */
In this solution we separate the array elements with a comma, Let's examine the regEx:
/0 means that every sub array starts with a 0, / is the beginning of the match
,[1-9]\d* this sub pattern match an the integer if it has a comma in front; the first digit cannot be 0, the other, optional, digits do not have this limitation. Thus we match ,1 or ,200 or ,9 or ,549302387439209.
We have to include in the subarray all the consecutive non zero number we find (,[1-9]\d*)* maybe none hence the second *.
`/g' closes the RegExp. The g indicates we want all the matches and not just the first one.
If you prefer an oneliner:
x.join(",").match(/0(,[1-9]\d*)*/g).map(x=>x.split(','));
or, if you prefer the pre ECMA2015 function expression syntax:
x.join(",").match(/0(,[1-9]\d*)*/g).map(function(x){return x.split(',');});

You could use a new array for every found zero value, otherwise append to the last array in the result set.
var array = [0, 1, 2, 0, 0, 0, 1, 0],
result = array.reduce(function (r, a) {
if (a) {
r[r.length - 1].push(a);
} else {
r.push([a]);
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

LeetCode Javascript - Remove Duplicates from a Sorted Array - Return Array issue

LeetCode problem 26 - Remove Duplicates From Sorted Array
Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same.
Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.
Return k after placing the final result in the first k slots of nums.
Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.
var removeDuplicates = function (nums) {
// Iterating the full array
for (let i = 0; i < nums.length; i++) {
// Checking for the repeating number
if (nums[i] === nums[i + 1]) {
// Removing the element which is repeating
nums = nums.slice(0, i + 1).concat(nums.slice(i + 2));
// Resetting the index after removing the element
i--;
}
}
console.log(nums);
return nums.length;
};
console.log(removeDuplicates([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4]));
This is the output of the above JS code on Visual Studio Code.
I am not able to submit this code to LeetCode. The expected output is the edited nums array, whereas my output doesn't match. How do I resolve this issue?
Your solution is too complicated. To remove duplicates from an array, just do:
const removeDuplicates = (arr) => [...new Set(arr)]
console.log(removeDuplicates([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4]));

Method that takes an array and moves all of the zeros to the end, preserving the order of the other elements

I am trying to understand why this part -- arr.splice(index, 1).push(0) -- of my code is not working.
why can't I splice and then push?
var moveZeros = function (arr) {
const newArray = arr.map((element, index) => element === 0 ? arr.splice(index, 1).push(element) : console.log(index, element))
}
Array.splice() returns an array of the elements that are removed from the array on which splice() was called on. Find documentation here:
Array.splice() documentation
You can achieve your goal by following code snippet:
let arr = [1, 0, 2, 0, 1, 0, 1, 0, 1, 0]
var moveZeros = function (arr) {
let zeroes = [];
let a = arr.reduce((finalArray, element) => {
element === 0 ? zeroes.push(element) : finalArray.push(element)
return finalArray;
}, []);
return [...a, ...zeroes];
}
console.log(moveZeros(arr));
splice returns an array of the removed elements. If you do
arr.splice(index, 1).push(0)
then you're pushing to the array of removed zeros (and that expression is subsequently discarded immediately afterwards).
You also shouldn't mutate an array while iterating over it - that'll make for very confusing behavior.
Consider filtering out zeros, while pushing to an array of zeros instead, and then combine the two arrays afterwards:
const moveZeros = (arr) => {
const zeros = [];
return arr
.filter((elm) => {
if (elm === 0) {
zeros.push(0);
return false;
}
return true;
})
.concat(zeros);
};
console.log(moveZeros([0, 1, 2, 3]));
console.log(moveZeros([3, 2, 1,0, 1, 2, 3]));
You could take a sorting instead and move all zeroes to the end. This approach rely on modern user agents which keep a stable sorting for equal (the onces whoes callback return zero) elements.
var array = [0, 1, 9, 2, 0, 0, 3, 5, 4, 2];
array.sort((a, b) => !a - !b);
console.log(array)

Javascript-extract numbers from array using for and if

How can I extract the numbers from the array 2, 5, 9, 15, 0, 4. that are bigger than 3 and smaller than 10 using for and if
Just think about iterating through your given array and check each element if it matches your conditions.
If element matches your conditions push it into a new or helper array.
Do the same with non matching elements, just in case you need them somehow.
If there is no use for your non-matching elements just do nothing.
var arr = [2, 5, 9, 15, 0, 4];
var matchingArr = []; // array for values bigger 3 and smaller 10
var nonMatchingArr = []; // array for values smaller 3 and bigger 10
arr.forEach(function(e){
if (e > 3 && e < 10) {
matchingArr.push(e);
} else {
nonMatchingArr.push(e);
}
});

Giving an original array and a new array that each element's value indicates the count of elements on the left that are bigger

Giving an array, say [4,2,1,3,5], based on this array, we have a new array, which each number shows the count of elements on its left that are bigger than itself, which is [0,1,2,1,0]. Now write a function with given input of [0,1,2,1,0], return the original array. The range of array is 1 ~ n (n is the size of the array, you can assume all numbers in the original array are consecutive if sorted)
Now to recover the original array, I have tried a way to solve the problem by iterating through the range of array from the end to the front like this:
My approach:
say the range is 1 ~ 5, the original array would be [1, 2, 3, 4, 5] if sorted. Iterate from the end to the beg,
so first 5, there is no element can be bigger than 5, so its maximum count of bigger elements would be 0, then 4 would have 1 as its maximum count of bigger elements, 3 to 2, etc. Store the key-value pairs into an object.
Now iterating through the input from back to front,
0 -> 5
1 -> can be 4, 3, or 2
2 -> can be either 3, 2, or 1
1 -> any number bigger than the first one.
0 -> (can be anything, since 5 is taken, so it can be either 1, 2, 3, or 4)
Simply to map each element of the input as value to its key from the map is not enough. What would be an intuitive way to approach this with optimal performance? (avoiding O(n ^2) if possible.)
Initially make an AVL Tree from numbers 1 to n.
Start from rear i.e. at nth index (considering 1 based index).
Now the high level outline level of the algorithm should look like this:
1. At any ith index, say the number in array(not the originial array) is j
2. Search the number which is at (i-j)th position in your AVL tree(This can be done in O(logn) time. Comment if you need more explanation on this)
3. The element in the AVL tree is your required element. Delete that element from AVL tree.(O(logn))
So the total complexity would be O(nlogn).
Walkthrough
Initially the tree will contain all 5 elements.
You start at index 5(1-based indexing). Element is 0, i.e. i=5, j=0. So 5th largest element which is 5.
Now the tree contains four elements 1,2, 3, and 4. i=4, j=1. So 4-1 i..e 3rd largest element which is 3 in this case.
i=3, j=2. (3-2)rd largest element is 1 since the tree contains (1, 2, 4).
And so on.
Using Tree to find the ith largest number
We can do this by, storing the count of number of nodes in left subtree at the root node. So consider a tree, having elements 1, 2, 3,4 and 5 and tree structure as following:
4(3)
/ \
3(1) 5(0)
/ \
1(0) 2(0)
At root, number 4 is the value and the number in round bracket has the number of nodes in left subtree.
While constructing(insertion and deletion too) the tree, we can maintain the count.
Now, to find the ith node, say we want suppose 3rd nodes in the given tree. We start with the root, it says it has 3 elements smaller than it to the left so we move to left. Now the root i.e. 3 has 1 smaller left element which is less than 3(ith element) so we move to right of it. Subtract 1(the left count)+1(the root itself) out of 3. Now the root is 2 we want 1st element, the left count is 0. Hence the 1st element of the subtree rooted at 2 is 2.
Basic pseudocode is below:
while(true){
count = root->leftCount;
if((count+1)<i){
//move to right
i-=(count+1);
root = root->right;
}
else if(i==(count+1)){
//root is the ith node
break;
} else{
//move to the levft
root=root->left
}
}
You could use Array#reduceRight and use the value as negative index for generating the original array.
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
result = countLeft.reduceRight(function (r, a) {
return array.splice(array.length - 1 - a, 1).concat(r);
}, []);
console.log(result);
Shorter version with ES6 and reverse base array.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
indices = array.map((_, i) => i),
result = [];
countLeft.forEach(a => {
result.unshift(array[indices[a]]);
indices = indices.filter((_, i) => i !== a);
});
console.log(result);
At last a proposal with complexity between O(n*(n-1)/2) and O(n).
This version uses a lazy array with progressive reduction of the length for every iteration. At the end, the offset array has zero elements.
var array = [5, 4, 3, 2, 1],
countLeft = [0, 1, 2, 1, 0],
result = [],
length = array.length;
countLeft.forEach((offset => (offset.length = countLeft.length, a => {
var i = offset[a] || 0;
result.unshift(array[i + a]);
offset.length--;
while (i < offset.length) {
offset[i] = (offset[i] || 0) + 1;
i++;
}
}))([]));
console.log(result);
A linear version, heavily inspired by the proposal of Oriol
var array = [1, 2, 3, 4, 5],
countLeft = [0, 1, 2, 1, 0],
swap = [],
i = 0,
l,
temp;
while (i < countLeft.length) {
l = countLeft[i];
while (l) {
swap.push(i + l - countLeft[i]);
l--;
}
i++;
}
i = swap.length;
while (i--) {
temp = array[swap[i]];
array[swap[i]] = array[swap[i] - 1];
array[swap[i] - 1] = temp;
}
console.log(array);
Here is a possible solution. See inline comments for a brief description of this method.
var a = [0,1,2,1,0],
n, b = [], res = [];
// build b = [5,4,3,2,1]
// we use this array to keep track of values to be pushed in res[],
// sorted in descending order
for(n = a.length; n > 0; n--) {
b.push(n);
}
// for each element of a, starting from the end:
// find correct value in b and remove it from b
while(a.length) {
res.push(b.splice(a.pop(), 1)[0]);
}
res = res.reverse();
console.log(res);
Output:
[4, 2, 1, 3, 5]
I propose an approach based on a custom sort, based on mergesort:
Split the array of inversions into two halves
Sort each part recursively, from greatest to lowest, maintaining stability
Merge the two parts
The difference with mergesort is the merge part. If we choose the j-th element of right part instead of the i-th of the left one, it will advance some elements, and therefore its number of inversions must be reduced by that amount.
Like mergesort, the complexity is O(n log n)
function undoInversions(inversions) {
function reorder(arr, from=0, to=arr.length) {
// Based on a stable decreasing mergesort
if(from >= to) return []; // Unusual base case
if(to === from + 1) return [arr[from]]; // Base case
var m = Math.floor((from + to)/2);
var l = reorder(arr, from, m), // Left recursive call
r = reorder(arr, m, to), // Right recursive call
ret = [], i=0, j=0;
while(i < l.length && j < r.length) { // Merge
if(r[j].value - l.length + i >= l[i].value) {
r[j].value -= l.length - i; // Reduce number of inversions
ret.push(r[j++]);
} else {
ret.push(l[i++]);
}
}
while(i < l.length) ret.push(l[i++]); // Merge remaining, if any
while(j < r.length) ret.push(r[j++]); // Merge remaining, if any
return ret;
}
var array = new Array(inversions.length);
reorder(inversions.map(function(inv, idx) {
return {value: inv, originalIndex: idx}; // Keep track of indices
})).forEach(function(obj, idx) {
if(obj.value !== 0) throw 'Invalid input';
array[obj.originalIndex] = idx + 1; // Invert the permutation
});
return array;
}
console.log(JSON.stringify(undoInversions([0,1,2,1,0])));
Here is an example to understand how it works:
[0,1,2,1,0] ~ [4,2,1,3,5]
⤩ ⤧
[0,0,2,1,0] ~ [2,4,1,3,5]
⤩ ⤧
[0,1,0,1,0] ~ [2,1,4,3,5]
⤩ ⤧
[0,0,0,1,0] ~ [1,2,4,3,5]
⤩ ⤧
[0,0,0,0,0] ——→ [1,2,3,4,5]
That is, each array of inversions corresponds to a permutation. We apply a permutation σ which transforms the input to the array of inversions [0,0,0,0,0], which corresponds to the permutation [1,2,3,4,5]. Since we kept track of the original indices, now we only need the to apply σ⁻¹ to [1,2,3,4,5] in order to get the permutation corresponding to the input.

Convert JSON data into format in Highcharts' basic line chart

Using Javascript, I am trying to convert some JSON data into the format used in Highcharts' basic line chart.
What I have to start with:
originalArray = [
['valueA', 1],
['valueA', 0],
['valueB', 9],
['valueB', 9],
['valueB', 3],
['valueC', 11]
]
And what I'm trying to create using the above:
desiredArray = [{
name: 'valueA',
data: [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}, {
name: 'valueB',
data: [0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0]
}, {
name: 'valueC',
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
}]
For some additional context, the 0-11 in originalArray[i][1] references a month (0 = January), and the desiredArray is a list of unique names and a count of their occurrences by month.
So far, I can:
Convert the data into a new array of objects
For each unique name in originalArray
Create a new object in the desiredArray, and set the name attribute
Add a data attribute that contains an empty array
But then I run into trouble, and can't figure out how to:
Loop through the originalArray
If the name in the originalArray matches the name in the desiredArray
Increment a counter in the matching seriesArray[i].data array, using the value of originalArray[i][1] as the index (it always be 0-11).
So I'm asking:
What's a good way to iterate across my originalArray, match up unique values, and then act only on those matches to push to the desiredArray.
What's the best way to increment the counters in desiredArray[i].data
I'm open to using libraries, such as underscore.js. Have been trying to figure this out for a couple of days now, so pretty much anything goes within the bounds of Javascript.
Updated with proper array initialization, now.
var max = originalArray.reduce(function(p,c){return Math.max(p,c[1]);},0);
var initSums = function(size) {
var arr = new Array(size);
for (var i=0;i<size;i++)
arr[i]=0;
return arr;
}
var map = originalArray.reduce(function(sums,val){
if (!sums.hasOwnProperty(val[0])) {
sums[val[0]] = initSums(max+1);
}
sums[val[0]][val[1]]++;
return sums;
},{});
var desiredArray = Object.keys(map).map(function(key) {
return {name: key, data: map[key]};
});
What we're doing here is a multi-step process:
Decide how big our arrays are going to need to be, by first scanning for the largest value in the original array.
Use an object to aggregate the counts (using Array.reduce()).
Transform the object and its properties into an array of name/data pair objects (using Array.map).
Edit: An improvement on S McCochran's solution, skipping the extraneous search for the maximum value in originalArray, since there should always be 12 elements of each data array, one per month.
function formatForHighcharts(array) {
// Create a map from value name to array of month counts
var map = originalArray.reduce(function(sums, pair) {
var key = pair[0], val = pair[1];
if(!(key in sums))
sums[key] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// val is the month index, which corresponds directly to array index, so increase that
sums[key][val]++;
return sums;
}, {});
// Map the object to an array of { name: ..., data: ... } pairs
var formatted = Object.keys(map).map(function (key) {
return { name: key, data: map[key] };
});
return formatted;
}
Usage:
var desiredArray = formatForHighcharts(originalArray);

Categories

Resources