D3: converting from string to number with unary + sign is not working - d3.js

I have a CSV file that I have imported.
after import, I have used nest and rollup to organize the data like below.
var data_group = d3.nest()
.key(function(d) {return +d.year;})
.key(function(d) {return +d.average_rating;})
.rollup(function (count) {
return count.length;
})
.entries(data);
console.log(data_group)
However, when I look at the console.log, the year and the the average_rating are strings.
I would like them to be a number.
How can I achieve this? Below is a picture of the output.
Also, can someone explain why they are not numbers after the + sign?

This is exactly as expected, as described in the d3.nest documentation. It also states explicitly in the documentation of nest.key that "The key function ... must return a string identifier".
The first few lines of the example given there look like so:
[{key: "1931", values: [
{key: "Manchuria", values: [
{yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm"},
{yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca"},
...
In particular, note that the first line starts key: "1931", even though 1931 is an integer value in the input data. That 1931 appears as an integer in the result only when it's a value, as in the lat two lines.
It's probably also worth pointing out that d3.nest is long since deprecated and was even removed in V6 several years ago. You should seriously consider using d3.group and/or d3.rollup in V7.

Related

Office Script - Split strings in vector & use dynamic cell address - Run an excel script from Power Automate

I'm completely new on Office Script (with only old experience on Python and C++) and I'm trying to run a rather "simple" Office Script on excel from power automate. The goal is to fill specific cells (always the same, their position shouldn't change) on the excel file.
The power Automate part is working, the problem is managing to use the information sent to Excel, in excel.
The script take three variables from Power automate (all three strings) and should fill specific cells based on these. CMQ_Name: string to use as is.
Version: string to use as is.
PT_Name: String with names separated by a ";". The goal is to split it in as much string as needed (I'm stocking them in an Array) and write each name in cells on top of each other, always starting on the same position (cell A2).
I'm able to use CMQ_Names & Version and put them in the cell they're supposed to go in, I've already make it works.
However, I cannot make the last part (in bold above, part 2 in the code below) work.
Learning on this has been pretty frustrating as some elements seems to sometime works and sometimes not. Newbie me is probably having syntax issues more than anyting...  
function main(workbook: ExcelScript.Workbook,
CMQ_Name: string,
Version: string,
PT_Name: string )
{
// create reference for each sheet in the excel document
let NAMES = workbook.getWorksheet("CMQ_NAMES");
let TERMS = workbook.getWorksheet("CMQ_TERMS");
//------Part 1: Update entries in sheet CMQ_NAMES
NAMES.getRange("A2").setValues(CMQ_Name);
NAMES.getRange("D2").setValues(Version);
//Update entries in sheet CMQ_TERMS
TERMS.getRange("A2").setValues(CMQ_Name);
//-------Part 2: work with PT_Name
//Split PT_Name
let ARRAY1: string[] = PT_Name.split(";");
let CELL: string;
let B: string = "B"
for (var i = 0; i < ARRAY1.length; i++) {
CELL = B.concat(i.toString());
NAMES.getRange(CELL).setValues(ARRAY1[i]);
}
}
  I have several problems:
Some parts (basically anything with red are detected as a problem and I have no idea why. Some research indicated it could be false positive, other not. It's not the biggest problem either as it seems the code sometimes works despite these warnings.
Argument of type 'string' is not assignable to parameter of type '(string | number | boolean)[ ][ ]'.
I couldn't find a way to use a variable as address to select a specific cell to write in, which is preventing the for loop at the end from working.  I've been bashing my head against this for a week now without solving it.
Could you kindly take a look?
Thank you!!
I tried several workarounds and other syntaxes without much success. Writing the first two strings in cells work, working with the third string doesn't.
EDIT: Thanks to the below comment, I managed to make it work:
function main(
workbook: ExcelScript.Workbook,
CMQ_Name: string,
Version: string,
PT_Name: string )
{
// create reference for each table
let NAMES = workbook.getWorksheet("CMQ_NAMES");
let TERMS = workbook.getWorksheet("CMQ_TERMS");
//------Part 0: clear previous info
TERMS.getRange("B2:B200").clear()
//------Part 1: Update entries in sheet CMQ_NAMES
NAMES.getRange("A2").setValue(CMQ_Name);
NAMES.getRange("D2").setValue(Version);
//Update entries in sheet CMQ_TERMS
TERMS.getRange("A2").setValue(CMQ_Name);
//-------Part 2: work with PT_Name
//Split PT_Name
let ARRAY1: string[] = PT_Name.split(";");
let CELL: string;
let B: string = "B"
for (var i = 2; i < ARRAY1.length + 2; i++) {
CELL = B.concat(i.toString());
//console.log(CELL); //debugging
TERMS.getRange(CELL).setValue(ARRAY1[i - 2]);
}
}
You're using setValues() (plural) which accepts a 2 dimensional array of values that contains the data for the given rows and columns.
You need to look at using setValue() instead as that takes a single argument of type any.
https://learn.microsoft.com/en-us/javascript/api/office-scripts/excelscript/excelscript.range?view=office-scripts#excelscript-excelscript-range-setvalue-member(1)
As for using a variable to retrieve a single cell (or set of cells for that matter), you really just need to use the getRange() method to do that, this is a basic example ...
function main(workbook: ExcelScript.Workbook) {
let cellAddress: string = "A4";
let range: ExcelScript.Range = workbook.getWorksheet("Data").getRange(cellAddress);
console.log(range.getAddress());
}
If you want to specify multiple ranges, just change cellAddress to something like this ... A4:C10
That method also accepts named ranges.

How to get documents that contain sub-string in FaunaDB

I'm trying to retrieve all the tasks documents that have the string first in their name.
I currently have the following code, but it only works if I pass the exact name:
res, err := db.client.Query(
f.Map(
f.Paginate(f.MatchTerm(f.Index("tasks_by_name"), "My first task")),
f.Lambda("ref", f.Get(f.Var("ref"))),
),
)
I think I can use ContainsStr() somewhere, but I don't know how to use it in my query.
Also, is there a way to do it without using Filter()? I ask because it seems like it filters after the pagination, and it messes up with the pages
FaunaDB provides a lot of constructs, this makes it powerful but you have a lot to choose from. With great power comes a small learning curve :).
How to read the code samples
To be clear, I use the JavaScript flavor of FQL here and typically expose the FQL functions from the JavaScript driver as follows:
const faunadb = require('faunadb')
const q = faunadb.query
const {
Not,
Abort,
...
} = q
You do have to be careful to export Map like that since it will conflict with JavaScripts map. In that case, you could just use q.Map.
Option 1: using ContainsStr() & Filter
Basic usage according to the docs
ContainsStr('Fauna', 'a')
Of course, this works on a specific value so in order to make it work you need Filter and Filter only works on paginated sets. That means that we first need to get a paginated set. One way to get a paginated set of documents is:
q.Map(
Paginate(Documents(Collection('tasks'))),
Lambda(['ref'], Get(Var('ref')))
)
But we can do that more efficiently since one get === one read and we don't need the docs, we'll be filtering out a lot of them. It's interesting to know that one index page is also one read so we can define an index as follows:
{
name: "tasks_name_and_ref",
unique: false,
serialized: true,
source: "tasks",
terms: [],
values: [
{
field: ["data", "name"]
},
{
field: ["ref"]
}
]
}
And since we added name and ref to the values, the index will return pages of name and ref which we can then use to filter. We can, for example, do something similar with indexes, map over them and this will return us an array of booleans.
Map(
Paginate(Match(Index('tasks_name_and_ref'))),
Lambda(['name', 'ref'], ContainsStr(Var('name'), 'first'))
)
Since Filter also works on arrays, we can actually simple replace Map with filter. We'll also add a to lowercase to ignore casing and we have what we need:
Filter(
Paginate(Match(Index('tasks_name_and_ref'))),
Lambda(['name', 'ref'], ContainsStr(LowerCase(Var('name')), 'first'))
)
In my case, the result is:
{
"data": [
[
"Firstly, we'll have to go and refactor this!",
Ref(Collection("tasks"), "267120709035098631")
],
[
"go to a big rock-concert abroad, but let's not dive in headfirst",
Ref(Collection("tasks"), "267120846106001926")
],
[
"The first thing to do is dance!",
Ref(Collection("tasks"), "267120677201379847")
]
]
}
Filter and reduced page sizes
As you mentioned, this is not exactly what you want since it also means that if you request pages of 500 in size, they might be filtered out and you might end up with a page of size 3, then one of 7. You might think, why can't I just get my filtered elements in pages? Well, it's a good idea for performance reasons since it basically checks each value. Imagine you have a massive collection and filter out 99.99 percent. You might have to loop over many elements to get to 500 which all cost reads. We want pricing to be predictable :).
Option 2: indexes!
Each time you want to do something more efficient, the answer lies in indexes. FaunaDB provides you with the raw power to implement different search strategies but you'll have to be a bit creative and I'm here to help you with that :).
Bindings
In Index bindings, you can transform the attributes of your document and in our first attempt we will split the string into words (I'll implement multiple since I'm not entirely sure which kind of matching you want)
We do not have a string split function but since FQL is easily extended, we can write it ourselves bind to a variable in our host language (in this case javascript), or use one from this community-driven library: https://github.com/shiftx/faunadb-fql-lib
function StringSplit(string: ExprArg, delimiter = " "){
return If(
Not(IsString(string)),
Abort("SplitString only accept strings"),
q.Map(
FindStrRegex(string, Concat(["[^\\", delimiter, "]+"])),
Lambda("res", LowerCase(Select(["data"], Var("res"))))
)
)
)
And use it in our binding.
CreateIndex({
name: 'tasks_by_words',
source: [
{
collection: Collection('tasks'),
fields: {
words: Query(Lambda('task', StringSplit(Select(['data', 'name']))))
}
}
],
terms: [
{
binding: 'words'
}
]
})
Hint, if you are not sure whether you have got it right, you can always throw the binding in values instead of terms and then you'll see in the fauna dashboard whether your index actually contains values:
What did we do? We just wrote a binding that will transform the value into an array of values at the time a document is written. When you index the array of a document in FaunaDB, these values are indexes separately yet point all to the same document which will be very useful for our search implementation.
We can now find tasks that contain the string 'first' as one of their words by using the following query:
q.Map(
Paginate(Match(Index('tasks_by_words'), 'first')),
Lambda('ref', Get(Var('ref')))
)
Which will give me the document with name:
"The first thing to do is dance!"
The other two documents didn't contain the exact words, so how do we do that?
Option 3: indexes and Ngram (exact contains matching)
To get exact contains matching efficient, you need to use a (still undocumented function since we'll make it easier in the future) function called 'NGram'. Dividing a string in ngrams is a search technique that is often used underneath the hood in other search engines. In FaunaDB we can easily apply it as due to the power of the indexes and bindings. The Fwitter example has an example in it's source code that does autocompletion. This example won't work for your use-case but I do reference it for other users since it's meant for autocompleting short strings, not to search a short string in a longer string like a task.
We'll adapt it though for your use-case. When it comes to searching it's all a tradeoff of performance and storage and in FaunaDB users can choose their tradeoff. Note that in the previous approach, we stored each word separately, with Ngrams we'll split words even further to provide some form of fuzzy matching. The downside is that the index size might become very big if you make the wrong choice (this is equally true for search engines, hence why they let you define different algorithms).
What NGram essentially does is get substrings of a string of a certain length.
For example:
NGram('lalala', 3, 3)
Will return:
If we know that we won't be searching for strings longer than a certain length, let's say length 10 (it's a tradeoff, increasing the size will increase the storage requirements but allow you to do query for longer strings), you can write the following Ngram generator.
function GenerateNgrams(Phrase) {
return Distinct(
Union(
Let(
{
// Reduce this array if you want less ngrams per word.
indexes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
indexesFiltered: Filter(
Var('indexes'),
// filter out the ones below 0
Lambda('l', GT(Var('l'), 0))
),
ngramsArray: q.Map(Var('indexesFiltered'), Lambda('l', NGram(LowerCase(Var('Phrase')), Var('l'), Var('l'))))
},
Var('ngramsArray')
)
)
)
}
You can then write your index as followed:
CreateIndex({
name: 'tasks_by_ngrams_exact',
// we actually want to sort to get the shortest word that matches first
source: [
{
// If your collections have the same property tht you want to access you can pass a list to the collection
collection: [Collection('tasks')],
fields: {
wordparts: Query(Lambda('task', GenerateNgrams(Select(['data', 'name'], Var('task')))))
}
}
],
terms: [
{
binding: 'wordparts'
}
]
})
And you have an index backed search where your pages are the size you requested.
q.Map(
Paginate(Match(Index('tasks_by_ngrams_exact'), 'first')),
Lambda('ref', Get(Var('ref')))
)
Option 4: indexes and Ngrams of size 3 or trigrams (Fuzzy matching)
If you want fuzzy searching, often trigrams are used, in this case our index will be easy so we're not going to use an external function.
CreateIndex({
name: 'tasks_by_ngrams',
source: {
collection: Collection('tasks'),
fields: {
ngrams: Query(Lambda('task', Distinct(NGram(LowerCase(Select(['data', 'name'], Var('task'))), 3, 3))))
}
},
terms: [
{
binding: 'ngrams'
}
]
})
If we would place the binding in values again to see what comes out we'll see something like this:
In this approach, we use both trigrams on the indexing side as on the querying side. On the querying side, that means that the 'first' word which we search for will also be divided in Trigrams as follows:
For example, we can now do a fuzzy search as follows:
q.Map(
Paginate(Union(q.Map(NGram('first', 3, 3), Lambda('ngram', Match(Index('tasks_by_ngrams'), Var('ngram')))))),
Lambda('ref', Get(Var('ref')))
)
In this case, we do actually 3 searches, we are searching for all of the trigrams and union the results. Which will return us all sentences that contain first.
But if we would have miss-spelled it and would have written frst we would still match all three since there is a trigram (rst) that matches.

What does this syntax mean (...) [duplicate]

This question already has answers here:
What are these three dots in React doing?
(23 answers)
Closed 4 years ago.
I'm putting my hands into reason-react.
In the following code :
let component = ReasonReact.statelessComponent("Component3");
let make = (~name, _children) => {
...component,
render: self => <input type_="checkbox" />,
};
I don't understand what (...) means on line 3.
When I delete it I get an error message :
The record field component can't be found.
If it's defined in another module or file, bring it into scope by:
- Annotating it with said module name: let baby = {MyModule.age: 3}
- Or specifying its type: let baby: MyModule.person = {age: 3}
The representation is called Spread Syntax. This was introduced in ES6.
Definition and example from MDN Docs. Link at the bottom.
Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// expected output: 6
console.log(sum.apply(null, numbers));
// expected output: 6
More details at : Spread Syntax

adding a unique id to an url

I have some difficulty in understanding how to add an id to an url for sending a request to a server. In fact, my main problem is the position of quotation mark after the equal sign in the third open method. Why it is not used just after Math.random() or just after .asp. Since, if i place the quotation mark just after math.random(), it works but just before math.random() does not. I want to understand what the quotation mark changes here...
xhttp.open(method, url, async);
xhttp.send();
xhttp.open("GET", "demo_get.asp", true);
xhttp.send();
**xhttp.open("GET", "demo_get.asp?t=" + Math.random(), true);**
xhttp.send();
For example, I understand what is happening in the following url.
http://localhost/test.php?q=_&p1=_&p2=_
? lets the server know what the ?_GET variables start
q, p1, and p2 are parameters and _ is value
The Math.random() function returns a float value. You are actually building up a string. So you need to convert it like so:
xhttp.open("GET", "demo_get.asp?t=" + Math.random().toString(), true);
The XMLHttpRequest object xhttp sends an asynchronous GET request to the server side script demo_get.asp with a query string t; the value of which is a random number (within the range 0 to 1).
In case of multiple query strings, the query strings are separated using &. For example, the test.php script you mentioned in your question accepts three query strings: q, p1 and p2, values of which are mentioned using = symbol. Most importantly, the query string-value pair are separated using & symbol.
That is because as it will pass a value like this [ url?t=(a random value) ] as a string value. Here the passing value is "demo_get.asp?t= RANDOM_NUMBER". And now coming to your question--------- if the quotation mark is just before the comma like [ "demo_get.asp?t= Math.random()" ] you can see that Math.random() is no more a function. It will become just a string.
Now you can do like that as new javascript has given a mechanism to do that:
Just type demo_get.asp?t= ${Math.random()} (within back-tick(``) as here back tick is not working in comment) instead of [ "demo_get.asp?t=" + Math.random() ]. I hope this helps :)

How can I set the cell in a jqGrid if the name in colmodel is an integer?

jqgrid setcell causes issues when the name in colmodel is an integer. It gives error:
Uncaught TypeError: Cannot read property 'formatter' of undefined.
My sequence of adding grid.locale-en.js and jqgrid-min.js is Right.
To be Precise the error is in the line :
var h = a.p.colModel[e]; if (h.formatter !== void 0)
in jqgrid-min.js where e the colmodel name (note this is an integer).
This is the way I am setting cell.
$("#GoingCostLogGrid").jqGrid('setCell', rowId, Costs_ProjectOrProgram , cellValue_CurrentTotalforAllFY);
Where Costs_ProjectOrProgram is an integer also the column name in colmodel.
There are large difference if you call setCell with number (like 12) as parameter or with the string which contains the number (like "12"). The number parameter will be interpreted as the index in colModel array, but the string value will be interpreted as column name.
So if the type of Costs_ProjectOrProgram is number and you want to interpret it as string then you should convert the variable to the string. So you should use
$("#GoingCostLogGrid").jqGrid('setCell', rowId, String(Costs_ProjectOrProgram),
cellValue_CurrentTotalforAllFY);
instead of
$("#GoingCostLogGrid").jqGrid('setCell', rowId, Costs_ProjectOrProgram,
cellValue_CurrentTotalforAllFY);
By the way, it's very bad to use variables with the first capital character. Corresponds to the name conversion of JavaScript one should use such names only for constructors (constructors of the class). It makes easy to see how to use the identifier. For example you need use var d = new Date(2015, 3, 24); and not var d = Date(2015, 3, 24); even if you could have the same results in many web browsers in the case of the above usage of Date. You can see, that even the formatter of JavaScript code displays the identify, which have the first capital character, in another color to stress that it have another meaning as the standard variable (cellValue_CurrentTotalforAllFY for example). I strictly recommend you to hold the name conversion existing in the language which you use.
UPDATED: jqGrid do the following: at the beginning of the code of setCell it try to convert column name parameter to number using isNaN(colname). If the column name is number or string which contains the number then it will be interpreted as the index of the column (see the lines). So to solve the problem you have to use setCell, getCell, getCol and many other methods with the index of the column instead of the name. So you have to do the following
var colModel = $("#GoingCostLogGrid").jqGrid("getGridParam", "colModel"),
iCol, nCol = colModel.length;
for (iCol = 0; iCol < nCol; iCol++) {
if (colModel[iCol].name === Costs_ProjectOrProgram) {
$("#GoingCostLogGrid").jqGrid("setCell", rowId, iCol,
cellValue_CurrentTotalforAllFY);
}
}
If you have many places which need to do the same then you can place the searching of column index by the column name in the function and to call if when it's required.
If you would use free jqGrid instead of jqGrid 4.6 then you can use jsonmap even in case of usage datatype: "local". See the wiki article for more details. In the way you can define the column as {name: "c5", jsonmap: "5"} instead of usage {name: "5"} and to have the described above problems. Alternatively you can change the input data so that the properties of input data will starts with a letter.

Resources