I have an items object with Items like this:
class Item {
/**
*#type {number}
*/
id;
}
let items = {};
I want to document the fact that keys of items is value.id is there a way ot do so?
Currently I'm using this syntax.
/**
* #type {{[item_id:number]:Item}}
*/
let items = {};
You can use Record
/**
* #type {Record<Item['id'], Item>}
*/
let items = {};
Related
Working on a web app - on a registration page -
want to make sure that the same id for two different registrations is never a case! - 4 - 5 people are going to use the app!
code :
function nextId(lastId){
var next = parseInt(lastId.split('-')[1]) + 1;
return ("MORCP-"+ (next).toString());
}
function serverNextRegId(){
/** --------------------------------------
* Registration sheet
* idCol : returns the dynamic column value of Id from headColumns in the sheet
* lastRow : row value as in sheet
----------------------------*/
var registration = MS.magicSheet("market","Registration");
var idCol = MS.msCol(registration,"Id");
var lastRow = MS.msLastRow(registration);
/**--------------------------
* connection sheet
* working id col
* lastRow from
--------------------------*/
var connection = MS.magicSheet("logs","Connection");
var workingIdCol = MS.msCol(connection,"Working Reg");
var lastRowConn = MS.msLastRow(connection,workingIdCol);
/** ---------------------
* get data from both sheets
--------------------------*/
var lastId = registration.getRange(lastRow,idCol).getValue();
var workingIds = connection.getRange(1,workingIdCol,lastRowConn).getValues().flat();
/** ----------------------------------
* check new id, if it's already in use
* if in use () nextId
--------------------------------- */
var catchNewId = nextId(lastId);
var check = workingIds.indexOf(catchNewId);
while(check>0){
catchNewId = nextId(catchNewId);
check = workingIds.indexOf(catchNewId);
}
var newRow = lastRowConn+1;
/**----------------------------------
* print the working value to the connection
--------------------------------------*/
connection.getRange(newRow,workingIdCol).setValue(nextId);
}
but at last the value prints is the whole function nextId() as string
Could someone explain it for me ?
and let me add : at first I tried
var next = (lastId)=>("MORCP-"+ (parseInt(lastId.split('-')[1]) + 1).toString());
within the function serverNextRegId()
Is it fine to use it here ?
Answer
Change .setValue(nextId) to .setValue(nextId())
Explanation
.setValue() seems to call .toString() on its argument. .toString() called on a function will return the source code of that (user defined) function
I have thousands of rows of data in a Google Sheets File in a column that looks something like
[{"amountMax":49.99,"amountMin":49.99,"availability":"true","color":"Brown","currency":"USD","dateSeen":["2019-04-11T08:00:00Z"],"isSale":"false","offer":"Online only","sourceURLs":["https://www.walmart.com/ip/SadoTech-Model-CXR-Wireless-Doorbell-1-Remote-Button-2-Plugin-Receivers-Operating-500-feet-Range-50-Chimes-Batteries-Required-Receivers-Beige-Fixed-C/463989633"]}]
I would like to be able to return the max value, the currency, the color attributes. How can I do that in Google Sheets. Ideally would like to do something like being able to retrieve the data attributes how I would normally in javascript like in this link here https://repl.it/#alexhoy/WetSlateblueDribbleware
However this does not seem to work for me when creating a function in script.google.com
For example, here is a slugify function which takes an input (cell) and turns it into a slug/handle without the need for looping. In Google Sheets I can then call =slugify(b2) and turn that value into slug form
/**
* Converts value to slug
* #customfunction
*/
function slugify(value) {
/*
* Convert the the vs in a range of cells into slugs.
* #customfunction
*/
let slug = '';
slug = value.substring(0, 100).toLowerCase();
slug = slug.replace(/[^\w\s-]/g, '');
slug = slug.replace(/\s+/g, '-');
Logger.log(slug);
return slug;
}
I want to do the same thing without looping to parse the object data above or declaring a range of values and what not.
Any suggestions on how I can do this in a simple way like shown above without the need for declaring active spreadsheet, range values and looping.
The following script will give you an idea about how to approach this task.
It assumes that:
the json data described in your question is in Cell A2.
the max value will be inserted into cell D2
the currency will be inserted into cell E2
the color will be inserted into cell F2
The script uses temporary arrays to capture the values and then assign it to a 2d array.
If you have many rows of data, then you will need to create a loop. I suggest that you build the arraydata progressively, and only update the target range at the end of the loop. This will give you the most efficient outcome.
function so6031098604() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var content = JSON.parse(sheet.getRange("A2").getValue());
// temp arrar to capture the data
var temparray = [];
temparray.push(content[0]["amountMax"]);
temparray.push(content[0]["currency"]);
temparray.push(content[0]["color"]);
// second array to accept the row data
var arraydata =[];
arraydata.push(temparray)
// define the target range
var targetrange = sheet.getRange(2, 4, 1, 3);
// update with the arraydata
targetrange.setValues(arraydata);
}
You want a custom function that will return certain fields from a JSON array.
In the following example, the target cell can be a single cell or an array.
This example does not use an arrayformula. The mechanics of using an arrayformula with a custom function may be something that you can research here Custom SHEETNAME function not working in Arrayformula.
Note: A 30 second quota applies to the execution of a Custom function
/**
* gets the MaxAmount, Current and Color from the data
*
* #param {cell reference or range} range The range to analyse.
* #return amountMax,currency and color
* #customfunction
*/
function getJsonData(range) {
//so6031098606
// Test whether range is an array.
if (range.map) {
// if yes, then loop through the rows and build the row values
var jsonLine = [];
for (var i = 0; i < range.length; i++) {
var jsonValues=[];
var v = JSON.parse(range[i][0]);
jsonValues.push(v.amountMax);
jsonValues.push(v.currency);
jsonValues.push(v.color);
// aggregate the row values
jsonLine.push(jsonValues);
} // end i
return jsonLine;
} else {
// if no, then just return a single set of values
var v = JSON.parse(range);
var jsonValues = [];
jsonValues.push(v.amountMax);
jsonValues.push(v.currency);
jsonValues.push(v.color);
return [jsonValues];
}
}
I have the following array containining values [8,0,7]. I would like to build a singly linked list for these array values, that is sequenced in the same order.
I have a ListNode object for each node in the linked list, which contains the value and a next link to the next node in the linked list. My code for building the linked list currently looks like this:
for(let i=0; i<results.length; i++){
console.log("n:",results[i])
if(!result){
result = new ListNode(results[i])
}else{
console.log("else",results[i])
result.next = new ListNode(results[i]);
}
}
For some reason the result linked list adds only 7 and not 0.
If I understand correctly, you're wanting to sequentially link ListNode objects by a single reference, in which case you'll need to update the result reference with the most recent ListNode() appended to the linked list, per iteration:
/* Added to support code snippet */
function ListNode(value) {
this.value = value;
this.next = '';
}
/* Input data */
const results = [8, 0, 7];
/* Track the latest list node appended to the linked list (ie, the head) */
let result = "";
for (let i = 0; i < results.length; i++) {
if (!result) {
result = new ListNode(results[i])
}
else {
result.next = new ListNode(results[i]);
console.log(`${result.value} -> ${result.next.value}`);
/* Update result reference to newly appended list node */
result = result.next
}
}
Another way to express this linking process more concisely would be via Array#reduce():
/* Added to support code snippet */
function ListNode(value) {
this.value = value;
this.next = '';
}
/* Reduce array to a linked list */
[8, 0, 7].reduce((prev, value) => {
/* Create node for current value */
const node = new ListNode(value);
/* If previous node exists, link it to current node */
if(prev) {
prev.next = node;
console.log(`${prev.value} -> ${node.value}`);
}
/* Return current node which will be the "previous" on
the next iteration */
return node;
}, "");
I have a page with a series of <a> links on it that look like this:
click me
These parameters manipulate the content for the eventual page linked to ("other.jsf") in minor ways.
As part of an A/B/n test I want to change the parameters in the href based on user behaviour on the page containing the link. So, for example, I'd want to change the fruit parameter to "orange" before it was clicked.
My problem is that the parameters change location in the string or may not exist at all for some links and the .param() function only appears to work on urls.
So far (based on another question answer) I have this, but it doesn't account for the possibility that there may be no "end_pos" or if "fruit=" is missing from the href (though this second bit seems like an easier fix with an if undefined-type function):
$('a').each(function () {
if ($(this).attr('href').indexOf('other') > -1) {
var hrefStr = $(this).attr('href');
var start_pos = hrefStr.indexOf('fruit=') + 1;
var end_pos = hrefStr.indexOf('&',start_pos); //works as long as fruit=apple is in the middle or front of the string
var fruit = hrefStr.substring(start_pos,end_pos);
...
//put modified href back in <a>
}
});
My second problem is then identifying the same part of the original href to put the new string back into. I can probably get this after understanding the method for extracting it in the first place though.
I should also say I have no control over the .jsf pages other than via JavaScript
Instead of substr, split the params apart and work with them. Here's verbose example to help.
var url = 'http://www.domain.com/page/other.jsf?fruit=apple&tree=pine&rock=sedimentary';
/**
* Gets params from url as an object
* #param {String} url
* #return {Object}
*/
var getParams = function (url) {
var params = {};
var match = url.match(/\?(.*)$/);
if (match && match[1]) {
match[1].split('&').forEach(function (pair) {
pair = pair.split('=');
params[pair[0]] = pair[1];
});
}
return params;
};
/**
* Converts an object of params into a query string.
* #param {Object} params
* #return {String}
*/
var paramsToString = function (params) {
return Object.keys(params || {}).map(function (key) {
return key + '=' + params[key];
}).join('&');
};
/**
* Replaces url params.
* #param {String} url
* #param {Object} newParams
* #return {String}
*/
var changeParams = function (url, newParams) {
var params = getParams(url);
Object.assign(params, newParams);
return url.replace(/\?(.*)$/, function () {
return '?' + paramsToString(params);
});
};
var urlWithNewParams = changeParams(url, {
fruit: 'banana',
weasel: 'yes',
});
console.log(urlWithNewParams); // http://www.domain.com/page/other.jsf?fruit=banana&tree=pine&rock=sedimentary&weasel=yes
You could use Attribute Contains Selector [name*="value"]
"a[href*=fruit]" to select a elements that contain "fruit" at href attribute, .attr(attributeName, function) , String.prototype.replace() to replace "apple" with "orange"
$("a[href*=fruit]").attr("href", function(_, href) {
return href.replace(/apple/, "orange")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
click me
I used this function to do that :
function getQueryParameters (str) {
return (str || document.location.search).replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0];
}
var queryParams = getQueryParameters(myUrl);
result :
{
"fruit": "apple",
...
}
Heres a way I use to extract url parameters!
var getUrlParameter = function(sParam,url) {
var sPageURL = url;
if(typeof(sPageURL) === 'undefined' || sPageURL == ''){
sPageURL = decodeURIComponent(window.location.search.substring(1));
}
var sURLVariables = sPageURL.split('&'), sParameterName, i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : decodeURI(sParameterName[1]);
}
}
};
usage
getUrlParameter('fruit',url_after_question_mark);
Part 2 of your problem!
If your parameters are known or atleast a known set you could always reconstruct the url with fresh values.
e.g.
var params = ['fruit','tree','some_other','some_other2'];//superset of all possible parameters.
Now you could loop through this array and call getUrlParameter function with each of the parameter to see if it exists. If it exists reconstruct your url with fresh values.
Just for clarification, the function getUrlParameters will return undefined for any param not found, discard it based on typeof undefined and then reconstruct url with your new values
Newer browsers support the very simple const params = new URL(href).searchParams. And then retrieve values with params.get("query")
I'm a noob and wrote a whole program without knowing the easy way to find an element in an array...
my_array.indexOf("find this value");
Is indexOf a lot better than storing how many elements are in an array, and looping through the array until you find the element you want? I could have simplified my code alot.
I tried to make my lookups constant time by using multiple arrays, and storing the keys. It makes insertions/deletes slow because I have to update the keys though.
Should I have just used indexOf?
Thanks
The vast majority of the time you are much better off to use a native function that has been optimized over whatever solution you come up with. Aside from that, however, you said something about storing the amount of elements in the array. Not sure why you did that when arrays have a .length property.
Javascript basically has two types of collections: Arrays and hashmaps. Both are a bit special. The hash map is just an object with named properties. The keys are strings that you use to access the values directly. Here's an example:
// create the hash map
var hashMap = {};
// add a value
var key = "John Dillinger";
hashMap[key] = "Criminal";
// retrieve the value
var stuff = hashMap[key];
Javascript arrays have a double functionality. They are of course arrays, but are also stacks. A stack follows the "last in - first out" rule. Here's an example of an array and a stack:
// Array example
var anArray = []; // or: var anArray = new Array();
anArray[0] = "some value";
alert(anArray[0]); // pops up "some value"
// Stack example
var stack = [];
stack.push("first");
stack.push("second");
alert(stack.pop()); // pop up "second"
Finally, for some problems a linked list could come in handy. For that you use an object. Something like this:
var linkedList = {value: "stuff"};
linkedList.next = {value: "other"};
linkedList.next.next = {value: "yet another value"};
// Traverse the list
var node = linkedList;
while(node) {
alert(node.value)
node = node.next;
}
Given the problem that you describe, I would use a hash map. Just remember to choose the correct collection type for any given problem.
You could use a hash table implementation in javascript to map values to array indices.
Native functions should be faster since it would be the runtime-engine precompiled code.
However, indexOf wasn't implemented until version 1.6, meaning it doesn't work in jscript/IE afaik.
But I would just prototype a workaround for it in that case. native functions is usually your best option.
In your case however, it seems that you want a hashmap, which in js is just a regular object as Helgi pointed out.
It's probable that the implementation of the indexOf method just loops over the array until it finds the requested value because in the general case that's about all you can do. Using it would clean up your code but is unlikely to make it faster. (There are faster ways of searching arrays but they carry certain restrictions and/or up-front costs.)
You should use the right data structure for the job. Arrays are for situations where order is important. If you find yourself searching through them a lot you should probably be using a hash instead. Hashes are unordered but lookups happen in constant time (no searching).
I've implemented javascript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master
Here is the code:
/*
=====================================================================
#license MIT
#author Lambder
#copyright 2009 Lambder.
#end
=====================================================================
*/
var HashMap = function() {
this.initialize();
}
HashMap.prototype = {
hashkey_prefix: "<#HashMapHashkeyPerfix>",
hashcode_field: "<#HashMapHashkeyPerfix>",
initialize: function() {
this.backing_hash = {};
this.code = 0;
},
/*
maps value to key returning previous assocciation
*/
put: function(key, value) {
var prev;
if (key && value) {
var hashCode = key[this.hashcode_field];
if (hashCode) {
prev = this.backing_hash[hashCode];
} else {
this.code += 1;
hashCode = this.hashkey_prefix + this.code;
key[this.hashcode_field] = hashCode;
}
this.backing_hash[hashCode] = value;
}
return prev;
},
/*
returns value associated with given key
*/
get: function(key) {
var value;
if (key) {
var hashCode = key[this.hashcode_field];
if (hashCode) {
value = this.backing_hash[hashCode];
}
}
return value;
},
/*
deletes association by given key.
Returns true if the assocciation existed, false otherwise
*/
del: function(key) {
var success = false;
if (key) {
var hashCode = key[this.hashcode_field];
if (hashCode) {
var prev = this.backing_hash[hashCode];
this.backing_hash[hashCode] = undefined;
if(prev !== undefined)
success = true;
}
}
return success;
}
}
//// Usage
// creation
var my_map = new HashMap();
// insertion
var a_key = {};
var a_value = {struct: "structA"};
var b_key = {};
var b_value = {struct: "structB"};
var c_key = {};
var c_value = {struct: "structC"};
my_map.put(a_key, a_value);
my_map.put(b_key, b_value);
var prev_b = my_map.put(b_key, c_value);
// retrieval
if(my_map.get(a_key) !== a_value){
throw("fail1")
}
if(my_map.get(b_key) !== c_value){
throw("fail2")
}
if(prev_b !== b_value){
throw("fail3")
}
// deletion
var a_existed = my_map.del(a_key);
var c_existed = my_map.del(c_key);
var a2_existed = my_map.del(a_key);
if(a_existed !== true){
throw("fail4")
}
if(c_existed !== false){
throw("fail5")
}
if(a2_existed !== false){
throw("fail6")
}
Bon Appétit,
Lambder