Related
Which d3 scale function or method do I use to transform data
input = 13 // count of items to be had in an array
into an array where:
the middle value is 0,
left-side values are descending,
and right-side values are ascending
output = [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6] // 13 items
I bet there is an elegant solution yet I do not speak data science language to find it myself.
Items count can be even, then the middle is twice 0:
input = 6 // count of items to be had in an array
output = [-2, -1, 0, 0, 1, 2] // 6 items
You don't need a D3 scale: a D3 scale maps an input value (domain) to an output value (range).
As you explained, you just want to convert a number N into an array of N elements following a given rule. That said, you just need a function that generates the array, which can be done with d3.range (or even with plain JS, no D3 needed).
For instance:
function gauge(input) {
return d3.range(input--).map(d => ~~(d - input / 2));
};
console.log(gauge(13))
console.log(gauge(6))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
And here the plain JS version, no D3:
function gauge(input) {
return Array.from(Array(input--).keys()).map(d => ~~(d - input / 2));
};
console.log(gauge(13))
console.log(gauge(6))
I'm writing an algorithm that finds the number of possible sums from an array that contains unique values, and a second array that contains the quantity of each corresponding value in the first array. For example, the pair [10, 20, 50] and [1, 2, 1] indicates that the total number of elements that I am combining is actually [10, 20, 20, 50], because there are two instances of the number 20.
My algorithm is currently passing every test except one, and I cannot for the life of me figure out why, since it is passing other, more complicated pairings. Here is my algorithm so far:
function possibleSums(values, quantity) {
const sums = new Set([]);
//my recursive function that finds all possible combinations moving
//down from a specific starting index in the valueArrray:
const combinations = (valueArray, countArray, position, currentSum) => {
if (currentSum > 0) sums.add(currentSum);
for(let i = position; i < valueArray.length; i++){
if (countArray[i] === 0){
continue;
}
currentSum += valueArray[i];
//reduce the count of that value that is still available
countArray[i]--;
//send off a recursive call to find the sum with the next
//available value
combinations(valueArray, countArray, i, currentSum);
//return the original count since `i` is increasing past
//the current value's location in the valueArray
countArray[i]++;
}
}
for (let i = 0; i < values.length; i++){
//start the recursive function calls at each index in the value array
combinations(values, quantity, i, 0)
}
return sums.size
}
This algorithm passes array pairs like :
[3, 1, 1] and [111, 84, 104] with the expected output of 521
[1, 1, 1, 1, 1] and [9, 19, 18, 12, 19] with the expected output of 77
[1, 2, 3] and [2, 3, 10000] with the expected output of 30008
but is failing
[10, 50, 100, 500] and [5, 3, 2, 2] , outputting 96 when the expected output is 122
Can anyone spot what I am missing in my logic?
122 expected output is not too big a test case for logging :)
Let's log the parameters:
...
const combinations = (valueArray, countArray, position, currentSum) => {
console.log(countArray, position, currentSum)
if (currentSum...
We see this, which makes sense:
[ 0, 0, 0, 0 ] 3 1400
But then we also see:
[ 0, 0, 1, 0 ] 3 1400
[ 0, 1, 0, 0 ] 3 1400
...
[ 1, 1, 1, 0 ] 3 1400
which don't.
Changing the current argument during the iteration seems to be affecting the variable during other calls.
Changing
currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum);
to
//currentSum += valueArray[i];
...
combinations(valueArray, countArray, i, currentSum + valueArray[i]);
seems to do the trick.
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; }
I want to sort an array of integers by moving all elements with a value of 2 or greater to the end of the array. However, if I'm comparing two elements that are both 2 or greater, or that are both less than 2, then I want to keep them in their current order. I'm using the following comparison function with the native .sort():
function customSort(a, b) {
return (a >= 2 && b < 2) ? 1 : -1;
}
It seems to work as intended for the following cases:
[2, 3, 2, 0] => [0, 2, 3, 2]
[2, 3, 2, 0, 0, 0, 1, 0, 0, 0] => [0, 0, 0, 1, 0, 0, 0, 2, 3, 2]
But it looks like once I get past 10 elements, it seems to randomly order the elements that are less than 2, and the elements that are 2 or greater:
[2, 3, 2, 0, 0, 0, 1, 0, 0, 0, 0] => [0, 0, 0, 0, 0, 1, 0, 0, 3, 2, 2]
(expected result) => [0, 0, 0, 1, 0, 0, 0, 0, 2, 3, 2]
I assumed the -1 in the ternary operator would always keep the left element left and thus keep the order (as opposed to 0 which would understandably order them randomly). I'm aware there's a better way to sort without using the native .sort(), but I'm just curious about the behavior and if there's some way I could change the comparison function to get this working correctly with the native .sort().
A way to do it without sorting is to loop over it and append items greater that one to one array and other items to another and join them.
var arr = [2, 3, 2, 0, 0, 0, 1, 0, 0, 0, 0];
var shifted = [].concat.apply([],arr.reduce( function (arr, val, ind){
var ind = val > 1 ? 1 : 0;
arr[ind].push(val);
return arr;
},
[[],[]]));
I've done some console logging of what happens in the sort method and I've come to the conclusion that when the sort iterates over the array, it will reiterate over any elements that have moved position again with your comparison function. The fact that your conditional is a bit more specific, and in a large array makes it very hard to keep track of what's going on. I've used your first example and logged the result of the ternary operator and the array as it's being sorted. You get the following:
[2, 3, 2, 0]
-1
[2, 3, 2, 0]
-1
[2, 3, 2, 0]
1
[2, 3, 2, 2]
1
[2, 3, 3, 2]
1
[0, 2, 3, 2]
When you sort through [2, 3, 2, 0, 0, 0, 1, 0, 0, 0], it goes through 29 iterations. When you add that extra 0 at the end of the array, it goes through 58 iterations. During that extra 29 I'd imagine it'll shuffle the elements backwards and forwards using your conditionals. That may explain why you're not getting the results you expect.
Very similar to epascarello's answer, only it uses reduceRight to loop over the array backwards. Where a member >= 2 is encountered, it's spliced and pushed to the end of the array.
This should be efficient as the array is modified in place, no new array is created whereas epascarello's creates 6 additional arrays (including the result of concat).
var data = [2, 3, 2, 0, 0, 0, 1, 0, 0, 0, 0].reduceRight(function(acc, n, i, data) {
if (n >= 2) data.push(data.splice(i, 1));
return data;
}, null);
document.write(data); // [0, 0, 0, 1, 0, 0, 0, 0, 2, 3, 2]
In this case I have an array of 4 integers between 0 and 256 that need to be sorted ascending. eg:
[0, 12, 211, 4] when I sort the I get (of course): [0, 4, 12, 211]
I simply get the integer value by requesting Array[0] (first indexed)
now, my problem is; many times, there are equal values in the array. like:
[0, 0, 0, 12] // already sorted
In these cases I need to pick a random index from the topmost equal values (0,0,0), other possiblities are (after sorting):
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 0
[0, 1, 1, 4] // results in 0;
so I need to pick a random index from the topmost values in a ascending sorted array.
Is that to be done while sorting , or in a simpler way than a lot of if-elses?
Sorting
If speed is important (which you seem to suggest it is) then have you looked at sorting networks? I have found these to be incredibly fast when sorting small sets of numbers.
To sort with a sorting network:
Network for N=4, using Bose-Nelson
Algorithm.
CreationDate: Tue Feb 15 04:44:06 2011
Creator: perl module
Algorithm::Networksort version 1.05.
Network for N=4, using Bose-Nelson
Algorithm. Input line. Comparator size
1. Comparator size 2. There are 5 comparators in this network, grouped
into 3 parallel operations.
[[0,1],[2,3]] [[0,2],[1,3]] [[1,2]]
This is graphed in 4 columns.
Pseudo:
if [0] > [1] { swap(0, 1) }
if [2] > [3] { swap(2, 3) }
if [0] > [2] { swap(0, 2) }
if [1] > [3] { swap(1, 3) }
if [1] > [2] { swap(1, 2) }
Finding Set of Indexes
Anyway this problem can be solved with a sort of divide and conquer (pseudo):
// First index is unique
if [0] != [1]
return 0
// First 2 are equal
else if [1] != [2]
return 0 or 1
// First 3 are equal
else if [2] != [3]
return 0 or 1 or 2
// All are equal
else
return 0 or 1 or 2 or 3
end
Or you can do this with a loop:
for i = 0 to 2
if [i] != [i+1]
return random(0 to i)
break loop
end if
loop
You should go for the algorithm which makes most semantic sense and is easiest to maintain probably over anything else, unless speed is crucial.
This will return a random index of equal values:
var myNums = new Array(211, 211, 211,211,214, 255);
myNums = myNums.sort();
if(myNums.length == 0)
alert("Array is zero sized");
else
{
var smallest = myNums[0];
var last=0;
var start = 0;
while(smallest == myNums[last])
last++;
last = last-1;
var randIndex = Math.floor(Math.random() *(last - start + 1)+ start);
alert(randIndex);
}
See it work here:
http://jsfiddle.net/rAbh3/
Making a for loop from right to left to select the elements will do the trick and if is done after the sorting process it will only add N to the complexity
Changing from nlogn to nlogn + n is not that much cpu expensive.
Edit:
The top most equal values in your example, shouldn't it be:
[211, 211, 211, 255] // results in 0 OR 1 OR 2
[13, 13, 125, 256] // results in 0 OR 1
[4, 211, 211, 255] // results in 1 or 2
[0, 1, 1, 4] // results in 1 or 2;
??