Format numbers to integers with SI prefix - javascript

I am drawing a chart, which gets dollar values (from 0 to millions), and I am trying to show nice ticks. I already used d3.nice to get 5 ticks that all have nice values, it's very cool. But since there's such a large variance, I am struggling to display my dollar values correctly.
I wish to do:
0-999: shows itself
1,000 - 999,999: shows 1k-999k (it's ok if 999,500 shows 1M, but not ok to show 1.00k by using d3.format('.3s'), or having 467k go to 400k by using d3.format('.1s'))
1,000,000 - 999,999,999: shows 1M-999M (also ok if it rolls over when rounding)
Prior to d3 version 4, this was easy. You could do:
.ticks((d) => {
var prefix = d3.formatPrefix(d);
return prefix(d).toFixed()+''+prefix.symbol;
})
But now, I am reading the d3 v4 docs after this fails on me, and it says:
The d3.formatPrefix method has been changed. Rather than returning an SI-prefix string, it returns an SI-prefix format function for a given specifier and reference value. For example, to format thousands:
var f = d3.formatPrefix(",.0", 1e3);
f(1e3); // "1k"
f(1e4); // "10k"
f(1e5); // "100k"
f(1e6); // "1,000k"
This seems impossible to accomplish now, then, because I want to vary the amount of significant digits, but I see no obvious way to accomplish that. Am I missing something simple?

If I understand your desired output correctly, you can use the number itself to define the value of d3.formatPrefix.
Look at this demo:
[1, 999, 1000, 999000, 1000000].forEach(function(d) {
var prefix = d3.formatPrefix(".0", d)
console.log(d + ": " + prefix(d))
})
<script src="https://d3js.org/d3.v4.min.js"></script>
PS: Using this approach, 999500 will give you 1000k, not 1M.

Okay well I suppose this was easy enough, but a little hacky. Here's the formatter function I used to make this behaviour:
(val) => {
let prefix = d3.formatPrefix('.3s', val);
let str = prefix(val);
if (val === 0) {
return '0';
}
return parseInt(str.slice(0, str.length - 2))+''+str.slice(str.length - 1);
}
I would be happy to use something better so if you have a cleaner answer, post and I'll accept.

Related

Correctly format a whole number to a 2 decimal places output in JS but put 0 in front [duplicate]

I have a script which returns a price for a product. However, the price may or may not include trailing zeros, so sometimes I might have 258.22 and other times I might have 258.2. In the latter case, I need to add the trailing zero. How would I go about doing this?
You can use javascript's toFixed method (source), you don't need jQuery. Example:
var number = 258.2;
var rounded = number.toFixed(2); // rounded = 258.20
Edit: Electric Toolbox link has succumbed to linkrot and blocks the Wayback Machine so there is no working URL for the source.
Javascript has a function - toFixed - that should do what you want ... no JQuery needed.
var n = 258.2;
n.toFixed (2); // returns 258.20
I don't think jQuery itself has any string padding functions (which is what you're looking for). It's trivial to do, though:
function pad(value, width, padchar) {
while (value.length < width) {
value += padchar;
}
return value;
}
Edit The above is great for strings, but for your specific numeric situation, rosscj2533's answer is the better way to go.

javascript object to string issue

I am using this code (from Harsha Jagadish) that does a quick little speed test to my URL of choice. It outputs the expected result just fine
return this.each(function() {
var g = foo();
$(this).text(g).append("Mb per second" + newg);
});
But what I need to do is grab that same output to compare if it's greater or less than a specific number. The issue is it's an object. I tried JSON.stringify and a couple of things, but no luck.
What am i missing?

Delete multiple documents

The following code is working but extremely slow. Up till the search function all goes well. First, the search function returns a sequence and not an array (why?!). Second, the array consists of nodes and I need URI's for the delete. And third, the deleteDocument function takes a string and not an array of URI's.
What would be the better way to do this? I need to delete year+ old documents.
Here I use xdmp.log in stead of document.delete just te be safe.
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.search(b, ['unfiltered']).toArray();
for (i=0; i<fn.count(c); i++) {
xdmp.log(fn.documentUri(c[i]), "info");
};
Doing the same with cts.uris:
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.uris("", [], b);
while (true) {
var uri = c.next();
if (uri.done == true){
break;
}
xdmp.log(uri.value, "info");
}
HTH!
Using toArray will work but is most likely were your slowness is. The cts.search() function returns an iterator. So All you have to do is loop over it and do your deleting until there is no more items in it. Also You might want to limit your search to 1,000 items. A transaction with a large number of deletes will take a while and might time out.
Here is an example of looping over the iterator
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.search(b, ['unfiltered']);
while (true) {
var doc = c.next();
if (doc.done == true){
break;
}
xdmp.log(fn.documentUri(doc), "info");
}
here is an example if you wanted to limit to the first 1,000.
fn.subsequence(cts.search(b, ['unfiltered']), 1, 1000);
Several things to consider.
1) If you are searching for the purpose of deleting or anything that doesnt require the document body, using a search that returns URIs instead of nodes can be much faster. If that isnt convenient then getting the URI as close to the search expression can achieve similar results. You want to avoid having the server have to fetch and expand the document just to get the URI to delete it.
2) While there is full coverage in the JavaScript API's for all MarkLogic features, the JavaScript API's are based on the same underlying functions that the XQuery API's use. Its useful to understand that, and take a look at the equivalent XQuery API docs to get the big picture. For example Arrays vs Iterators - If the JS search API's returned Arrays it could be a huge performance problem because the underlying code is based on 'lazy evaluation' of sequences. For example a search could return 1 million rows but if you only look at the first one the server can often avoid accessing the remaining 999,999,999 documents. Similarly, as you iterate only the in scope referenced data needs to be in available. If they had to be put into an array then all results would have to be pre-fetched and put put in memory upfront.
3) Always keep in mind that operations which return lists of things may only be bounded by how big your database is. That is why cts.search() and other functions have built in 'pagination'. You should code for that from the start.
By reading the users guides you can get a better understanding of not only how to do something, but how to do it efficiently - or even at all - once your database becomes larger than memory. In general its a good idea to always code for paginated results - it is a lot more efficient and your code will still work just as well after you add 100 docs or a million.
4) take a look at xdmp.nodeUrl https://docs.marklogic.com/xdmp.nodeUri,
This function, unlike fn.documentUri(), will work on any node even if its not document node. If you can put this right next to the search instead of next to the delete then the system can optimize much better. The examples in the JavaScript guide are a good start https://docs.marklogic.com/guide/getting-started/javascript#chapter
In your case I suggest something like this to experiment with both pagination and extracting the URIs without having to expand the documents ..
var uris = []
for (var result of fn.subsequence(cts.search( ... ), 1 , 100 )
uris.push(xdmp.nodeUri(result))
for( i in uris )
xdmp.log( uris[i] )

Javascript format a number asin C#

I have a number which currently is 1,657,108,700 and growing. However I wish for it to show as
1,657,108k
Does javascript or html have a build in function to do this?
The value is being set throu javascript to a span field in html.
[edit]
From the comment I got my method as far as:
var start = '1,657,108,700';
start = (start / 1000).toFixed(0);
var finish = '';
while (start.length > 3)
{
finish = ','.concat(start.substring(start.length - 3, 3), finish);
start = start.substring(0, start.length - 3);
};
finish = start + finish + "k";
return finish;
however this returns 1,65,7k instead of 1,657,108k.. anyone know why?
var formattedNumber = Math.round(yourNumber / 1000).toLocaleString() + "k";
Turn the above into a function or not as appropriate. I'm not aware of a single function to do this, or of a way to cater for non-English versions of "k" (assuming there are some), but at least toLocaleString() should take care of the comma versus fullstop for thousands issue.
UPDATE: I posted the above without testing it; when I tried it out I found toLocaleString() formatted 1234 as 1,234.00. I had thought of fixing it by using a regex replace to remove trailing zeros except of course I can't be sure what character toLocaleString() is going to use for the decimal point, so that won't work. I guess you could write some code that uses toLocaleString() on a "control" number (e.g., 1.1) to see at runtime what character it uses for the decimal.
UPDATE 2 for your updated question, inserting the commas manually, I did it like this:
var unformattedNumber = 123456;
var a = unformattedNumber.toString().split("");
for (var i=a.length-3; i >0; i-=3)
a.splice(i,0,",");
var formattedNumber = a.join("") + "k";

fastest way to detect if a value is in a group of values in Javascript

I have a group of strings in Javascript and I need to write a function that detects if another specific string belongs to this group or not.
What is the fastest way to achieve this? Is it alright to put the group of values into an array, and then write a function that searches through the array?
I think if I keep the values sorted and do a binary search, it should work fast enough. Or is there some other smart way of doing this, which can work faster?
Use a hash table, and do this:
// Initialise the set
mySet = {};
// Add to the set
mySet["some string value"] = true;
...
// Test if a value is in the set:
if (testValue in mySet) {
alert(testValue + " is in the set");
} else {
alert(testValue + " is not in the set");
}
You can use an object like so:
// prepare a mock-up object
setOfValues = {};
for (var i = 0; i < 100; i++)
setOfValues["example value " + i] = true;
// check for existence
if (setOfValues["example value 99"]); // true
if (setOfValues["example value 101"]); // undefined, essentially: false
This takes advantage of the fact that objects are implemented as associative arrays. How fast that is depends on your data and the JavaScript engine implementation, but you can do some performance testing easily to compare against other variants of doing it.
If a value can occur more than once in your set and the "how often" is important to you, you can also use an incrementing number in place of the boolean I used for my example.
A comment to the above mentioned hash solutions.
Actually the {} creates an object (also mentioned above) which can lead to some side-effects.
One of them is that your "hash" is already pre-populated with the default object methods.
So "toString" in setOfValues will be true (at least in Firefox).
You can prepend another character e.g. "." to your strings to work around this problem or use the Hash object provided by the "prototype" library.
Stumbled across this and realized the answers are out of date. In this day and age, you should not be implementing sets using hashtables except in corner cases. You should use sets.
For example:
> let set = new Set();
> set.add('red')
> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false
Refer to this SO post for more examples and discussion: Ways to create a Set in JavaScript?
A possible way, particularly efficient if the set is immutable, but is still usable with a variable set:
var haystack = "monday tuesday wednesday thursday friday saturday sunday";
var needle = "Friday";
if (haystack.indexOf(needle.toLowerCase()) >= 0) alert("Found!");
Of course, you might need to change the separator depending on the strings you have to put there...
A more robust variant can include bounds to ensure neither "day wed" nor "day" can match positively:
var haystack = "!monday!tuesday!wednesday!thursday!friday!saturday!sunday!";
var needle = "Friday";
if (haystack.indexOf('!' + needle.toLowerCase() + '!') >= 0) alert("Found!");
Might be not needed if the input is sure (eg. out of database, etc.).
I used that in a Greasemonkey script, with the advantage of using the haystack directly out of GM's storage.
Using a hash table might be a quicker option.
Whatever option you go for its definitely worth testing out its performance against the alternatives you consider.
Depends on how much values there are.
If there are a few values (less than 10 to 50), searching through the array may be ok. A hash table might be overkill.
If you have lots of values, a hash table is the best option. It requires less work than sorting the values and doing a binary search.
I know it is an old post. But to detect if a value is in a set of values we can manipulate through array indexOf() which searches and detects the present of the value
var myString="this is my large string set";
var myStr=myString.split(' ');
console.log('myStr contains "my" = '+ (myStr.indexOf('my')>=0));
console.log('myStr contains "your" = '+ (myStr.indexOf('your')>=0));
console.log('integer example : [1, 2, 5, 3] contains 5 = '+ ([1, 2, 5, 3].indexOf(5)>=0));
You can use ES6 includes.
var string = "The quick brown fox jumps over the lazy dog.",
substring = "lazy dog";
console.log(string.includes(substring));

Categories