Is there a simple way to group js array values by range? - javascript

if I have a js array like below, is there a simple way to re-group the array values by range, the logic is based on the range step, the range step is 1, so if the array values are continuous increased by 1, then it should be write like "1-3", otherwise it should be break to another group, thanks a lot!
var list = ["1", "2", "3", "5", "6", "9", "12", "13", "14", "15", "16"]
function(list) {
// * some function here //
return ["1-3", "5-6", "9", "12-16"]
}

You could use Array#reduce for it.
var array = ["1", "2", "3", "5", "6", "9", "12", "13", "14", "15", "16"],
result = array.reduce(function (r, a, i, aa) {
r.push(!i || aa[i - 1] - a + 1 ? a : r.pop().split('-')[0] + '-' + a);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Here is not as compact as #nina-scholz's version, but 70% faster.
It also allows creating only "non-redundant" ranges (ranges with minimum difference of 2 or greater, which might speed up decoding)
function getRanges(array, minRange, sort)
{
if (sort)
array = new Float64Array(array).sort();
let min = array[0],
max = min,
result = [];
minRange = minRange ? 1 : 0;
for(let i = 1, c = array.length + 1; i < c; i++)
{
const id = array[i];
if (max == id - 1)
{
max = id + ""; // + converting to string
continue;
}
if (max-min > minRange)
result.push(min + "-" + max);
else if (min != max)
result.push(min, max);
else
result.push(min);
min = max = id + ""; // + converting to string
}
return result;
}
//examples
var array = ["1", "2", "3", "5", "6", "9", "12", "13", "14", "15", "16"];
console.log(JSON.stringify(array));
console.log("ranges with minimum diference of 1:", JSON.stringify(getRanges(array)));
console.log("ranges with minimum diference of 2:", JSON.stringify(getRanges(array, true)));
array.sort(()=>.5-Math.random());//randomize array
console.log(JSON.stringify(array));
console.log("ranges with minimum diference of 1 sorted:", JSON.stringify(getRanges(array, false, true)));
console.log("ranges with minimum diference of 2 sorted:", JSON.stringify(getRanges(array, true, true)));
console.log("ranges with minimum diference of 1 unsorted:", JSON.stringify(getRanges(array)));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Roman and Arabic numbers string array sorting in JS

My array contains list of the Arabic and roman numbers as string. I would like to sort them by the order of Roman numbers ascending first then follows the Arabic numbers in ascending order.
I write the code as below,
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
try {
if (Number(a) != null)
a = Number(a);
if (Number(b) != null)
b = Number(b);
} catch (e) {}
if (a > b) {
return 1;
}
if (b > a) {
return -1;
}
if (a == b) {
return a.position - b.position;
}
});
console.log(myarray);
But the results are like,
Results: ii,iii,xv,x,1,2,3,5,v,vi,vii,vi,iv,xix,xiv,58,88,99,101,125,201,601,i
If I have not convert the string to numbers,
Results: 1,101,125,2,201,3,5,58,601,88,99,i,ii,iii,iv,v,vi,vi,vii,x,xiv,xix,xv
My expect result should be
Results: i,ii,iii,iv,v,vi,vi,vii,x,xiv,xv,xix,1, 2, 3,5,58,88,99,101,125,201,601
Multiple things here:
You don't need a try / catch block. Just leave it.
Checking the parsed number for null won't give you the expected result. You need to check for NaN.
Either parse both values to a number or none. Otherwise you can't compare properly.
Usually numbers will be returned before strings. To avoid this behaviour you need to invert the result value by multiplicating with -1, if one value is a number and one is a string.
Here is the working version of the sort method:
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
if (!isNaN(Number(a)) && !isNaN(Number(b))) {
a = Number(a);
b = Number(b);
}
var result;
if (a > b) {
result = 1;
}
if (b > a) {
result = -1;
}
if (a == b) {
result = a.position - b.position;
}
if (isNaN(Number(a)) !== isNaN(Number(b))) {
result = result * -1;
}
return result;
});
console.log(myarray);

Javascript Recursive function. Wrong results while storing data in array

While trying to get all permutations using Heap's algorithm, I met trouble storing the results in array.
The result generated is (from console.log(arr);)
["1", "2", "3"]
["2", "1", "3"]
["3", "1", "2"]
["1", "3", "2"]
["2", "3", "1"]
["3", "2", "1"]
but only the last value is stored in the arr, and the array stored somehow is this (from console.log(JSON.stringify(allPermutations)); )
["3", "2", "1"]
["3", "2", "1"]
["3", "2", "1"]
["3", "2", "1"]
["3", "2", "1"]
["3", "2", "1"]
var allPermutations = [];
function swap(arr,index1,index2){
var dummy = arr[index1];
arr[index1] = arr[index2];
arr[index2] = dummy;
return arr;
}
function generate(n,arr){
if(n==1){
console.log(arr);
//result:
//["1", "2", "3"]
//["2", "1", "3"]
//["3", "1", "2"]
//["1", "3", "2"]
//["2", "3", "1"]
//["3", "2", "1"]
allPermutations.push(arr);
}else{
for(var i=0;i<n-1;i++){
generate(n-1,arr);
if( n%2 ==0){
arr = swap(arr,i,n-1);
}else{
arr = swap(arr,0,n-1);
}
}
generate(n - 1, arr);
}
}
generate(3,['1','2','3']);
console.log(JSON.stringify(allPermutations));
/*result:
["3","2","1"]
["3","2","1"]
["3","2","1"]
["3","2","1"]
["3","2","1"]
["3","2","1"]*/
What's wrong with this? Would love to understand. Thanks
Replace allPermutations.push(arr) with allPermutations.push(arr.slice()).
The problem is, you keep pushing the same array, then changing that array. When you push an array, you don't push a copy of it: you push a reference. There is only one array, and six references to it; when you read them out, they all read the same, because they are all the same array.
.slice() will give you a new array with the same elements; this way, you get six new arrays into your result, instead of six mentions of the same array.
From one of my earlier answers that is almost-but-not-quite a duplicate, a metaphor I like for this:
As a metaphor, imagine a theatre director in casting. He turns to an actor, says "You... you'll be Romeo.". Then he looks at the same actor, says "You... you'll be Mercutio. Here, Mercutio, take this sword. Romeo... who told you to get a sword?!?" completely failing to realise that, if Romeo and Mercutio are the same person, if one of them picks up a sword, the other does it too.
That's because arrays are objects, and objects are passed by value, but that value is a reference.
Then, your code keeps pushing the same arr reference to allPermutations. But the values in that reference are modified later.
Instead, you should push a copy of the array. You can copy it with .slice().
var allPermutations = [];
function swap(arr, index1, index2) {
var dummy = arr[index1];
arr[index1] = arr[index2];
arr[index2] = dummy;
return arr;
}
function generate(n, arr) {
if (n == 1) {
allPermutations.push(arr.slice());
} else {
for (var i = 0; i < n - 1; i++) {
generate(n - 1, arr);
if (n % 2 == 0) {
arr = swap(arr, i, n - 1);
} else {
arr = swap(arr, 0, n - 1);
}
}
generate(n - 1, arr);
}
}
generate(3, ['1', '2', '3']);
document.write(JSON.stringify(allPermutations));

sorting array by predefined number

Let's say I have multidimensional array
var arr = [{
"id": "1",
"firstname": "SUSAN",
"dezibel": "91"
}, {
"id": "2",
"firstname": "JOHNNY",
"dezibel": "74"
}, {
"id": "3",
"firstname": "ANDREA",
"dezibel": "67"
}];
How can I sort it by "dezibel" but not ascending or descending, but closest to a giving number? For example,
var num = 78;
so target value is 78. and final sorting must be: 74, 67, 91.
You'll need to use a custom sort function that compares the absolute difference of each object's dezibel attribute from 78.
var arr = [{
"id": "1",
"firstname": "SUSAN",
"dezibel": "91"
}, {
"id": "2",
"firstname": "JOHNNY",
"dezibel": "74"
}, {
"id": "3",
"firstname": "ANDREA",
"dezibel": "67"
}];
num = 78;
arr.sort(
function(first,second){
var a = Math.abs(num - (+first.dezibel));
var b = Math.abs(num - (+second.dezibel));
return a - b;
});
alert(JSON.stringify(arr));
Write a sort function which calculates the distance to your number:
arr.sort(function(a, b){
return Math.abs(num-a) - Math.abs(num-b);
});
Use this to sort the dezibel properties in your array. It will calculate the distance between each of them and num. It will then select the smaller of the two distances, and continue in this manner to sort the whole array.
Just sort by the absolute difference.
var arr = [{ "id": "1", "firstname": "SUSAN", "dezibel": "91" }, { "id": "2", "firstname": "JOHNNY", "dezibel": "74" }, { "id": "3", "firstname": "ANDREA", "dezibel": "67" }],
num = 78;
arr.sort(function (a, b) {
return Math.abs(a.dezibel - num) - Math.abs(b.dezibel - num);
});
document.write('<pre>' + JSON.stringify(arr, 0, 4) + '</pre>');
.sort optionally takes a function. The function takes 2 values at a time, and compares them:
If the first value should sort higher than the second, the function should return a positive number.
If the first value should sort lower than the second, the function should return a negative number.
If the values are equal, the function should returns 0.
So, if you wanted to sort by dezibel in ascending order, you could do
arr.sort(function(a,b){
return a.dezibel- b.dezibel;
});
However, you want to sort by dezibel's distance from some number. To find the magnitude of the difference from 78 and the dezibel value, take the absolute value of the difference:
Math.abs(78 - a.dezibel)
Now, if we want to sort based on that value for each object, we can take the difference of that Math.abs call for both a and b:
arr.sort(function(a,b){
return Math.abs(78 - a.dezibel) - Math.abs(78 - b.dezibel);
});
You can use the array sort function for this:
arr.sort(function(a, b) {
return num - 1 * a.dezibel + num - 1 * b.dezibel
})
I would just add a distance and then sort it ...
num=78
for e in arr
e.dist=Math.abs(e.dezibel - num)
arr.sort (l,r) =>
if l.dist > r.dist then 1 else -1
delete the dist after
but put in one line is ok, too
arr.sort (l,r) =>
if Math.abs(l.dezibel - num) > Math.abs(r.dezibel - num) then 1 else -1

javascript - calculate and transform object key values into percentages (on 100%)

I have this object:
myObj = {
red: "332",
green: "632",
yellow: "14",
brown: "45",
pink: "83",
violet: "21",
gray: "100",
black: "33",
white: "200"
};
I need to calculate and transform these object key values into percentages (on 100%)
Is there a way to do it?
If you wish to get the percentage based off the highest number, here is an example with two loops (one to finding the max value and the other for changing actual values):
var res = document.getElementById('r');
var myObj = {
red: "332",
green: "632",
yellow: "14",
brown: "45",
pink: "83",
violet: "21",
gray: "100",
black: "33",
white: "200"
};
var max = 0;
for (var i in myObj) { // we find the max here
if (parseInt(myObj[i], 10) > max)
max = myObj[i];
}
for (var i in myObj) { // we do the conversion here
myObj[i] = (myObj[i] / max * 100) + "%";
res.innerHTML += "<br>" + i + ": " + myObj[i]; // html output
}
<div id="r"></div>
And if you wish to get the percentages of total (total being the sum of all values):
var res = document.getElementById('r');
var myObj = {
red: "332",
green: "632",
yellow: "14",
brown: "45",
pink: "83",
violet: "21",
gray: "100",
black: "33",
white: "200"
};
var total = 0;
for (var i in myObj) { // we find the total here
total += parseInt(myObj[i], 10);
}
for (var i in myObj) { // we do the conversion here
myObj[i] = (myObj[i] / total * 100) + "%";
res.innerHTML += "<br>" + i + ": " + myObj[i]; // html output
}
<div id="r"></div>
Note: you might want to round those numbers off to a fewer number of decimals.
First, you need to find the largest number. Then, you'll need to use that to find the percentages. So, say that you find that 632 is the largest number. Each other number's percentage will be that number divided by 632.

Making an array with 2 digit numbers in javascript

Let's say that i got a variable which it contains the number 19. I want to make an array from it with the following numbers
var positions = [ "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19" ];
How is this possible in javascript?
Something like :
var number = 19;
var arr = [];
for ( i = 0; i <= number ; i++ ) {
arr.push(i < 10 ? ("0" + i.toString()) : i.toString());
}
demo : http://jsfiddle.net/Kfnnr/1/
Alternatively:
var mynumber = 19,
myarr = String(Array(mynumber+1))
.split(',')
.map(function(el,i){return i<10 ? '0'+i : ''+i;});
For zeropadding you may want to use:
function padLeft(nr,base,chr){
base = base || 10;
chr = chr || '0';
var len = (String(base).length - String(nr).length)+1;
return len > 0? Array(len).join(chr)+nr : nr;
}
// usage
padLeft(1); //=> '01'
padLeft(1,100); //=> '001'
padLeft(1,10000,'-'); //=> '----1'
Update 2019: in es20xx String.prototype contains a native padStart method:
"1".padStart(2, "0"); //=> "01"
// ^ max length
// ^ fill string (or space if omitted)
essentially you want to pad 0's and the answers here will not suffice and scale when the number is changed.. Probably the better solution would be
function padZero(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
Using this example finally solved my own ##iterator function interface issue;
Thanks a lot
var myArray = [];
function getInterval (start, step, end, ommit){
for (start; start <= end; start += step) {
myArray.push( start < 10 ? ("" + start.toString()) : start.toString());
}
}
getInterval(2, 2, 20, 20);
myArray; // __proto__: Array
// (10) ["2", "4", "6", "8", "10", "12", "14", "16", "18", "20"]
myArray[4]; // "10"

Categories

Resources