I was playing with javascript trying to create a classic spiral matrix like this one:
01 02 03 04
12 13 14 05
11 16 15 06
10 09 08 07
But when I debugged my code, I noticed something weird:
The matrix array is filling the space [1][x] when j is 0
How is that possible?
What is the explanation for this behavior?
Here is the piece of code:
var draw = function(direction, dim) {
var directions = ['right', 'down', 'left', 'up'];
var __matrix = Array(dim[0]).fill(Array(dim[1]).fill(0));
var fill = {};
fill.right = function(nextIndex, matrix, j, i, value) {
if(value === (dim[0] * dim[1])) { return matrix; }
if(matrix[j][i] === 0) { matrix[j][i++] = value++; }
else { i--; nextIndex++; }
debugger;
return fill[directions[nextIndex]].apply({}, arguments);
}
fill.down = function() { }; //todo
fill.left = function() { }; //todo
fill.up = function() { }; //todo
return fill[direction](directions.indexOf(direction), __matrix, 0, 0, 1);
};
console.log(draw('right', [4,4]));
This has nothing to do with the increment operator. You are writing on the 0 index, it just so happens that every array in your matrix array is the same array reference.
var arr = Array(4).fill(Array(4).fill(0))
//arr[0] === arr[1] === arr[2] === arr[3]
arr[0][0] = 'Hey';
console.log(arr[1][0]) //also 'Hey'
Seems your problem in this line
Array(dim[0]).fill(Array(dim[1]).fill(0));
Array.fill makes shallow copy of the array
When you change one element of the array it will affect to all elements, because it reference to the same object.
See example below:
var matrix = Array(5).fill(Array(5).fill(0));
document.write('<br>before <pre>' + JSON.stringify(matrix) + '</pre>') ;
matrix[1][0] = 1;
document.write('<br>after <pre>' + JSON.stringify(matrix) + '</pre>') ;
It's diffcult to answer such a question without a fiddle, sorry...
I'd try to put some console.log() before and after the indicted statement (matrix[j][i++] = value++;), to understand what's happening...
Related
I have a problem to get an updated result out of a while loop.
Already spent a few days reading articles and trying to figure it out...
Hopefully you can help :)
I want to take a title (string) and if its length is less than 78, I want to add words to it randomlly,
Using a list of words in a sheet.
This is working prefectly, the problem is that its adding same words over and over again.
I searched for a way to remove duplicates and found the [...new Set(arr)], and It works perfectly.
Yet when I try to use it in a while loop (every loop check remove duplicates) , the end result still has duplicates.
I am adding the sheet which will help you understand it better,
Sheet
And here's the code.
/**
* Adds Brand, adds words to 80 letters.
*
* #Param Title the base title used
* #Param Brand the store brand (Ess,Eag..)
* #customfunction
*/
function AddKeyWords(title, brand) {
title = ChangeTitleWords(title, brand); //previous function.
//title = "Just a few words";
if(title == "Missing Brand" || title == "") {
return "Missing Brand or Title";
} else {
let wordsArr = title.split(" "); //split title to array
let len = title.length;
//If title is more than 80 notes, remove words of it.
while (wordsArr.join(" ").length > 80) {
wordsArr.pop();
}
let Title2 = wordsArr.join(" ");
let Title2Len = Title2.length;
let len2 = 80 - Title2Len;
//if title had less than 78 notes, add a random word
while (len2 > 3) {
let wordsArr1 = title.split(" ");
if (len2 == 3) {
wordsArr.push(random3());
} else if (len2 == 4) {
wordsArr.push(random4());
} else if (len2 >= 5 && len2 < 18) {
wordsArr.push(random5());
} else if (len2 >= 18) {
wordsArr.push(random10());
}
wordsArr = [...new Set(wordsArr)]; // create a new arr without duplicates
len2 = 80 - wordsArr.join(" ").length;
//return title3;
title3 = wordsArr;
}
return title3.join(" ");
}
}
////Random Functions.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function random3() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('L5:L').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random4() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('M5:M').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random5() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('N5:N').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
function random10() {
var t = SpreadsheetApp.getActive().getSheetByName('Settings').getRange('O5:O').getValues().filter(String)
return(t[getRandomInt(0, t.length-1)]);
}
The problem with your code is that you are pushing an array into an array and [...new Set()] does not work on nested arrays
You can easy see the problem implementing logs:
Logger.log("before: ");
Logger.log( wordsArr);
wordsArr = [...new Set(wordsArr)];
Logger.log("after: ");
Logger.log( wordsArr);
The result will be:
Jul 22, 2020, 1:51:06 PM Info before:
Jul 22, 2020, 1:51:06 PM Info [Just, a, few, words, [ggggggggggg], [ggggggggggg]]
Jul 22, 2020, 1:51:06 PM Info after:
Jul 22, 2020, 1:51:06 PM Info [Just, a, few, words, [ggggggggggg], [ggggggggggg]]
Mind that you will not see the same result if you implement
Logger.log("after: " + wordsArr); because concatenation will convert
the array automatically into a string.
Solution
There are many ways to convert your array into a 1-D string.
Without modifying the rest of your code, the easiest would probably be to use flat()
For this simply modify
wordsArr = [...new Set(wordsAr))];
to
wordsArr = [...new Set(wordsArr.flat())];
I have the following values.
var list = "09:05, 10:05, 12:30, 16:30 , ... , ..."
The type of values in the list is a regular string, not an object.
Based on this value, I want to divide from 0 to 12 am and from 13 to 23 pm.
Therefore, the result I want is as follows.(If you check the log value)
var am = am 09:05 , am 10:05
var pm = pm 12:30 , pm 16:30
It may be a simple question, but it is a very difficult problem for me as a script novice.
Please help me.
Create a sort function first
var sort = ( a, b ) => convertToMin( a ) - convertToMin( b );
var convertToMin = ( a ) => ( items = a.split( ":" ).map( Number ), items[ 0 ] * 60 + items[ 1 ] );
Now use reduce to segregate the array
var output = list.reduce( (a,b) => //using reduce to iterate, a is the accumulator and b is item in array for current iteration
( convertToMin(b) > 12*60 ? a.pm.push( b ) : a.am.push( b ), a ) ,
{ am :[], pm : [] }) ; //accumulator is initialized to { am :[], pm : [] }
output.am.sort( sort );
output.pm.sort( sort );
Demo
var list = ["09:05", "10:05", "12:30", "16:30"];
var sort = (a, b) => convertToMin(a) - convertToMin(b);
var convertToMin = (a) => (items = a.split(":").map(Number), items[0] * 60 + items[1]);
var output = list.reduce((a, b) =>
(convertToMin(b) > 12 * 60 ? a.pm.push(b) : a.am.push(b), a), {
am: [],
pm: []
});
output.am.sort(sort);
output.pm.sort(sort);
console.log(output);
Here's what I'd do to solve this problem.
Separate the values in the string into an array. I'd Google javascript split string into array. Nothing wrong with Googling stuff; even seasoned devs have to do it all the time! At least I do. :)
Then create a for loop that goes through each element of the array. A good search for how to do that is javascript for loop array.
Then for each element, split the string again (this time by the :).
Then convert the first part into a number (javascript convert string
to integer) and see whether it is bigger or smaller than 12.
You could adjusted value with am/pm time and sort it to the wanted array.
function format(v) { return ('0' + v).slice(-2); }
function getM(t) {
var v = t.split(':');
return (v[0] < 12 ? 'am' : 'pm') + ' ' + [v[0] % 12 || 12, v[1]].map(format).join(':');
}
var list = '09:05, 10:05, 12:30, 16:30',
am = [],
pm = []
result = { am: am, pm: pm };
list
.split(', ')
.map(getM)
.forEach(function (s) {
result[s.slice(0, 2)].push(s);
});
console.log(am);
console.log(pm);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Split the items using the appropriate separator, process them with a cycle then join them with the appropriate separator
var items = list.split(", ");
var ams = [];
var pms = [];
for (var index = 0; index < list.length; index++) {
var isPM = ((list[index].substring(0, 2) >= 12);
var currentArray = window[isPM ? "pms" : "ams"];
var found = false;
var val = (isPM ? "pm" : "am") + " " + items[index];
for (var innerIndex = 0; (!found) && (innerIndex < currentArray.length); innerIndex++) {
if (currentArray[innerIndex] > val) {
found = true;
currentArray.splice(innerIndex, 0, val);
}
}
if (!found) currentArray.push(val);
}
var am = ams.join(" , ");
var pm = pms.join(" , ");
Try with .split method like this,
Updated without jQuery
var list = "09:05, 10:05, 12:30, 16:30";
var options = list.split(',').map(time => {
h = time.split(':')[0];
return parseInt(h) >= 12 ? 'pm ' + time : 'am ' + time;
})
console.log(options);
I have this string:
002 2.0 (100aa) 95-97
I then want regex the 95-97 portion of it and paste it with relevant two numbers so I get a year.
In example, 96-97 should become 1995-1997, but 00-05 should become 2000-2005 (all numbers between 0 and 16 should be pasted with 20, but all other numbers with 19).
Then, when I have i.e. 1995-1997 I want to check if a year (i.e. 1996) is present inside 1995-1997 interval or not, and return a bolean.
How would one wright such code?
Best Regards
You could use the callback variant of replace:
function parseString(str) {
function padYear(year) {
return year < 30 ? 2000+year :
year < 100 ? 1900+year : year;
}
var result = {};
result.hasCurrentYear = false;
result.str = str.replace(/(\d\d)-(\d\d)$/g, function (match, yr1, yr2) {
yr1 = padYear(+yr1);
yr2 = padYear(+yr2);
var yrNow = (new Date).getFullYear();
result.hasCurrentYear = yrNow >= yr1 && yrNow <= yr2;
return yr1 + '-' + yr2;
});
return result;
}
var str = '002 2.0 (100aa) 95-16';
console.log(parseString(str));
Note that I made the split at year 30, as the solution will become outdated soon if you use 16 as split year.
I suppose there's a much simpler way to check if a certain year is in "range".The solution using String.split, Array.map functions and Number constructor:
var str = "002 2.0 (100aa) 95-97";
function checkYearInRange(str, year) {
year = Number(year);
var range = str.split(" ").pop().split('-').map((v) => Number((Number(v) > 16)? "19"+v : "20"+v));
return (range[0] <= year && year <= range[1]);
}
console.log(checkYearInRange(str, "1996")); // true
console.log(checkYearInRange(str, "2015")); // false
I've been trying to generate values for a dropdown where the user is to select hours, minutes.
To get the possible values of minutes, I was using this:
_.range(1, 61)
This generates an array like
[1, 2, 3 ... 60]
but I require it to be of form
[01, 02, 03 ... 60]
Is there a clean way to getting this using underscore?.
In FF, and soon in other browsers, use padStart:
_.range(1, 61) . map(num => String(num).padStart(2, '0'))
Another approach would be
_.range(1, 61) . map(num => ('0' + num).slice(-2));
This tacks a zero on the beginning of all the numbers, but then takes just the last two characters of the result.
A functional approach would be to write a higher-order function which takes the length and returns a function which pads its input to that length:
function make_padder(len) {
return str => ('0000000000000' + str).slice(-len);
}
Now you can write
_.range(1,61) . map(make_padder(2))
Place this at the top of your javascript file ...
Number.prototype.pad = function (count) {
var num = this.valueOf();
var ret = "";
for (var i = 0; i < count - num.toString().length; i++) {
ret += "0";
}
ret += num;
return ret;
}
Then return ...
_.range(1, 61).map(function (num) { return num.pad(2) })
This will give an array of strings with leading 0s of length 2
It is possible only with array of strings:
var
range = _.range(1, 61),
i,
arr = [];
for (i in range) {
if (range[i] <= 9) {
arr.push('0' + range[i]);
}
else {
arr.push('' + range[i]);
}
}
Result:
console.log(arr); // ["01", "02" ... "61"]
Object-Oriented JavaScript - Second Edition When a number starts with a 0, it's considered an octal number. For
example, the octal 0377 is the decimal 255:
var n3 = 0377;
typeof n3; // "number"
n3; // 255
The last line in the preceding example prints the decimal
representation of the octal value.
parseInt('0377', 8); // 255
ECMAScript 5 removes the octal literal values and avoids the confusion
with parseInt() and unspecified radix.
This question already has answers here:
Find the number in an array that is closest to a given number
(7 answers)
Closed 9 years ago.
I am fairly new to javascript and I'm having problems finding the most efficient way to calculate the problem below
I have an array of objects. Each object has a time stamp and a total field. I have a number saved as a variable and I want to loop through the array to find the timestamp of the object with the total field closest to my number.
This is a sorted array so the numbers are always increasing so for example the numbers could look like this:
Jan 125
Feb 150
Mar 200
Apr 275
If the number I have is 205 I would like to get the result Mar back.
They are objects taken from a mongoDb so look something like this
{TimeStamp: "2013-06-24 01:00", Delivered: 464, Queued: 39, Total: 503}
{TimeStamp: "2013-07-02 01:00", Delivered: 485, Queued: 37, Total: 522}
{TimeStamp: "2013-07-05 01:00", Delivered: 501, Queued: 41, Total: 542}
{TimeStamp: "2013-07-08 09:48", Delivered: 501, Queued: 64, Total: 565}
If the list is already sorted on the right field, you can use this code to find the minimum distance in O(n):
var data = [
{total: 125, name: 'Jan'},
{total: 150, name: 'Feb'},
{total: 200, name: 'Mar'},
{total: 275, name: 'Apr'}
];
function getClosest(arr, value)
{
var closest, mindiff = null;
for (var i = 0; i < arr.length; ++i) {
var diff = Math.abs(arr[i].total - value);
if (mindiff === null || diff < mindiff) {
// first value or trend decreasing
closest = i;
mindiff = diff;
} else {
// trend will increase from this point onwards
return arr[closest];
}
}
return null;
}
You keep track of the currently closest object and its corresponding (absolute) difference between the total and the searched value.
You keep updating those two values as long as the difference decreases. When that no longer happens you can return immediately, because you know it will never decrease afterwards.
To use it:
getClosest(data, 200);
I've got this helpful generic function:
function min(ary, key) {
return ary.map(function(x) {
return [key ? key(x) : x, x]
}).reduce(function(m, x) {
return x[0] < m[0] ? x : m;
})[1]
}
It finds a minimal element in the array using key as a comparison function. Applied to your problem:
number = ...
closestTimestamp = min(arrayOfRecords, function(record) {
return Math.abs(number - record.total)
}).TimeStamp;
var numbers = [122,231,323,53];
var myNumber = 200;
var difference = 9999;
var nearest = null;
for (i = 0 ; i < numbers.lenght; i++){
var candidate = numbers[i];
var currentDifference = Math.abs(myNumber - candidate);
if (currentDifference < difference) {
nearest = candidate; difference = currentDifference;
}
}
You can use a binary search for that value. Adapted from this answer:
function nearestIndex(arr, compare) { // binary search, with custom compare function
var l = 0,
r = arr.length - 1;
while (l <= r) {
var m = l + ((r - l) >> 1);
var comp = compare(arr[m]);
if (comp < 0) // arr[m] comes before the element
l = m + 1;
else if (comp > 0) // arr[m] comes after the element
r = m - 1;
else // this[m] equals the element
return m;
}
// now, l == r+1
// usually you would just return -1 in case nothing is found
if (l == arr.length) return r;
if (r == 0) return 0;
if (Math.abs(compare(arr[l])) > Math.abs(compare(arr[r]))) // "closer"
return r;
else
return l;
}
var items = […];
var i=nearestIndex(items, function(x){return x.Total-532;}); // compare against 532
console.log(items[i].TimeStamp);