Sum numbers in base 32 or 36 - javascript

I'd like to sum numbers that range from small (1) to very large (2**200). For that, I thought of using base-32 or base-36, such that the largest number would occupy a reasonable string (2**200 becomes bnklg118cog0000000000000000000000000000 in base-36).
However, how can I sum 2**200 with 1, for example? If I convert to decimal, it'll do 1.6069380442589901e+60 + 1 and the 1 will be ignored due to the precision. What I'd like is to do, for example:
bnklg118cog0000000000000000000000000000 + 1 = bnklg118cog0000000000000000000000000001
I know I can convert a number to base 36 using .toString(36), but summing them would sum strings, not giving the correct result.
Is it possible to do something like in hexadecimal, as (0xee + 0x11).toString(16) = "ff"?

Using intermediate BigInt (suggested by #robertklep), I came up with this solution: (I use here a conversion function from here to avoid problems with very large numbers)
/* Convert `value` given in a basis `radix` to BigInt
* (from https://stackoverflow.com/a/55646905/3142385)
* #param {string} value Number written in a given in basis `radix`
* #param {int} radix Basis of the number `value`
* #returns {BigInt} Converted BigInt number
*/
function ConvertToBigInt(value, radix) {
return [...value.toString()]
.reduce((r, v) => r * BigInt(radix) + BigInt(parseInt(v, radix)), 0n);
}
/* Sum two values in base `radix` and return new value on the same basis
* #param {string} value1 First value in base `radix` to be summed
* #param {string} value2 Second value in base `radix` to be summed
* #param {int} radix Basis of `value1` and `value2`
* #returns {string} Summed values in base `radix`
*/
function Sum(value1,value2, radix) {
return (ConvertToBigInt(value1.toString(),radix)+ConvertToBigInt(value2.toString(),radix)).toString(radix);
}
console.log(BigInt(2**200).toString(36),BigInt(2**199).toString(36),Sum("bnklg118comha6gqury14067gur54n8won6guf4","5tsaq0im6cb8n38dfdz0k033qfdkkbmgcbl8f7k",36))
// Output: "bnklg118comha6gqury14067gur54n8won6guf4" "5tsaq0im6cb8n38dfdz0k033qfdkkbmgcbl8f7k" "hhcw61juj0xpx9p4a5x1o09b7a4poyvd0yrp9mo"

Related

convert String to json object with wrong string

I have String like
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]"
need to convert into Json format
trying something like this
const text = "[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
const myArr = JSON.parse(text);
document.getElementById("demo").innerHTML = myArr[0];
But getting error :-
Uncaught SyntaxError: Expected property name or '}' in JSON at position 2
Simple Solution:
Convert all of the single-quoted strings and properties (that are invalid JSON in the specification) to valid JSON with double-quotes using a Regular Expression (RegExp):
const unformattedText = "[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]"
// convert with a regex
const formattedText = unformattedText.replace(/'/g, "\"")
console.log(formattedText)
// convert to object
const object = JSON.parse(formattedText)
console.log(object)
NOTE: As #Igor pointed out, this will only work for JSON strings that don't include properties or strings with single-quotes ', (like the OP's).
Update:
My String.prototype.replace() solution behavior:
Cannot recover any field or string
with a single-quote ' in it,
Example:
[{'name':'Bob O'Rielly','height':'13',5"'}]
Becomes:
[{"name":"Bob O"Rielly","height":"13",5""}]
which is incorrect and invalid JSON!
Over-Engineered Solution: Literally an entire parser...
It should be well commented enough to be able to use and modify to anyone's needs.
WARNING: In brute-force mode can be O(2^n)! Which is very inefficient.
The presence of incorrect string delimiters causes ambiguity during parsing. And useful solutions are very computationally expensive.
/**
* #typedef {function(StringPosition): boolean} BruteForce
* A function that takes information about the current
* position of the single-quote in the misformatted
* string the parser found.
*
* And returns a boolean whether the parser will use
* brute-force parsing, with time-complexity `O(2^n)`.
*
* #typedef {function(StringPosition): Guess} Heuristic
* A function that takes a reference the parser state
* information, and modifies the parser guess state
* using the given reference.
*
* This allows for much more complex logic.
*
* Example:
*
* Situations where the single-quote (`'`) needs to be
* replaced by an escaped double-quote (`\\"`).
*
* #typedef {("array"|"field"|"object-value"|"value")} JSONType
* A string union type JSON types for arrays, object
* fields, object values, and values.
*
* #typedef {("\""|"\'")} Quote
* One single-quote (`'`) or double-quote (`""`)
* string character.
*
* #typedef {("\\\""|"\\'")} EscapedQuote
* One single-quote (`\\'`) or double-quote (`\\"`)
* string character, preceded by a reverse solidus
* (`\`), to escape it from the usual JSON
* interpretation.
*
* #typedef {Object} StringPosition
* An object containing information on where in a
* string a single-quote character was found by the
* parser.
*
* #property {Number} StringPosition.stringIndex The index of the
* single-quote in the misformatted string the parser
* is currently trying to correct.
*
* #property {Number} StringPosition.characterIndex The UTF-32 character
* pseudo-index `index - (UFT-16 surrogate pairs /2)`
* of the single-quote in the misformatted string the
* parser is currently trying to correct.
*
* #property {String} StringPosition.followingString The JSON substring
* after the single-quote.
*
* #property {String} StringPosition.precedingString The JSON substring
* before the single-quote.
*
* #property {Guess} StringPosition.currentGuess An object containing
* information about a parser attempt to reformat a
* JSON string. It contains a reformatted substring
* the parser generated, and whether the internal
* heuristics consider the substring to end in any
* incompleted JSON type.
*
* #typedef {Object} Guess
* An object containing information about a parser
* attempt to reformat a JSON string. It contains a
* reformatted substring the parser generated, and
* whether the internal heuristics should consider the
* substring to end in an incompleted JSON string.
*
* #property {String} Guess.string The parser guess of how to reformat
* JSON string. It is set to the previously parsed
* guess, with future parser guesses appended to it.
*
* #property {Boolean} Guess.isString The internal flag for `preParse`
* that determines whether the algorithm interprets
* current parsing as parsing the middle of a JSON
* string value.
*
* #property {Boolean} Guess.isEscaped The internal flag for `preParse`
* that determines whether the algorithm interprets
* current parsing as parsing a character escape
* sequence in the middle of a JSON string value. It is
* a number to accommodate unicode escape sequences.
*
* Only works for: `\'`. not: `\"`, `\\`, `\/`, `\n`,
* `\r`, `\b`, `\f`, `\t` or `\uXXXX`.
*
* #property {Boolean} Guess.isObjectField The internal flag for
* `preParse` that determines whether the algorithm
* interprets the current parsing state as parsing
* the middle of a JSON object value.
*
* #property {Boolean} Guess.isObjectValue The internal flag for
* `preParse` that determines whether the algorithm
* interprets the current parsing state as parsing
* the middle of a JSON object field.
*
* #property {Number} Guess.isObject The internal flag for `preParse`
* that determines whether the algorithm interprets
* the current parsing state as parsing the middle of
* a JSON object. It is a number to track how many
* objects the single-quote is nested within.
*
* #property {Number} Guess.isArray The internal flag for `preParse` that
* determines whether the algorithm interprets the
* current parsing state as parsing the middle of a
* JSON array value. It is a number to track how many
* arrays the single-quote is nested within.
*
* #property {Number} Guess.aufoFilled The internal flag for `preParse`
* that determines whether the algorithm already
* reformatted this string index with a previous RegEx
* heuristic solution. Is a string for how many future
* indexes were prefilled. Default: `0`.
*
* #property {JSONType} [Guess.isJSONType] The internal flag for `preParse`
* that determines whether the algorithm interprets
* the current parsing state as parsing the middle of
* specific JSON type. It is a string union type for
* arrays, object fields, object values, and values.
* Used to keep track of the JSON type being parsed,
* without including type nesting depth information.
*/
/**
* #description Reformats a misformatted JSON string to valid JSON.
*
* **NOTE:** This function assumes that all
* double-quotes are escaped correctly, like: `\"`.
*
* If there are improperly escaped double-quotes, this
* function will not be able to reformat the
* misformatted JSON properly, unless custom logic in
* an `opts.heuristic` function is used.
*
* **NOTE:** this is _**NOT**_ designed for
* performance or large JSON strings. This can have
* `O(2^n)` time-complexity!
*
* #param {String} misformattedJSON The string that is invalidly
* formatted for JSON by using single-quotes (`'`)
* instead of double-qutoes (`"`) to delimit string
* values and object fields.
*
* #param {Heuristic} [heuristic] A function that takes a reference the
* parser state information, and modifies the parser
* guess state using the given reference. This allows
* for much more complex logic.
*
* Example:
*
* Situations where the single-quote (`'`) needs to be
* replaced by an escaped double-quote (`\\"`).
*
* #param {BruteForce} [bruteForce] A function that takes information
* about the current position of the single-quote in
* the misformatted string the parser found. And
* returns a boolean whether the parser will use
* brute-force parsing, with time-complexity `O(2^n)`.
*
* #param {Boolean} [TRY_HARD] A flag to brute-force with default,
* heuristics, with time-complexity at around:
* `O(2^n)`. Can be better than a `bruteForce`, but
* still not reccommended, use `heuristic` if possible.
*
* #returns {String[]} The reformatted string, or array of reformatted
* strings, if there was no error in parsing.
*/
function preParse(misformattedJSON, bruteForce, heuristic, TRY_HARD) {
/*
if (TRY_HARD)
console.warn(
"THIS HAS O(2^n) TIME COMPLEXITY, MAKE SURE YOU KNOW WHAT YOU'RE DOING..."
)
*/
/**
* #type {Guess[]} Array to store all of the parser's possible
* solutions. Holds strings of the parser guesses.
* Used when strings are ambiguous because of
* incorrect formatting, and brute-forcing. Is
* appended to by recursive calls to `preParse`.
*/
let guesses = [
{
string: "",
isString: false,
isEscaped: false,
isArray: 0,
isObject: 0,
isObjectField: false,
isObjectValue: false,
isJSONType: undefined,
aufoFilled: 0
}
]
/**
* #type {String[]} Fixed-width array to index each UTF-32 character.
* JavaScript strings are indexed by UTF-16 code units,
* which are 2 bytes (16 bits) in length, a single
* UTF-32 character is 4 bytes (32 bits), the same as
* a complete UTF-16 surrogate pair, (which is two
* UTF-16 code units).
*/
let fixedWidthCharacterArray = []
// iteration over each UTF-32 code point to add to UTF-32 array
for (const UTF16CodePoint of misformattedJSON)
fixedWidthCharacterArray.push(UTF16CodePoint)
// loop over UTF-32 characters
for (
let characterIndex = 0, stringIndex = 0;
characterIndex < fixedWidthCharacterArray.length;
stringIndex += fixedWidthCharacterArray[characterIndex].length,
characterIndex++
) {
/**
* #type {String} Current UTF-32 code point in character array.
*/
const character = fixedWidthCharacterArray[characterIndex]
// loop over all guesses, and evaluate thier likelihood,
// reversing over array to add or remove guesses
// without changing the portion of the array being looped over
for (let guessIndex = guesses.length - 1; guessIndex >= 0; guessIndex--) {
/**
* #type {Guess} Parsers's guess of how to reformat JSON string. It
* is set to the previous parser guess, with future
* parser guesses appended to it. It also includes
* state information used for the default heuristics.
*/
const currentGuess = guesses[guessIndex]
// check guess auto-fill
if (currentGuess.aufoFilled) {
// decrement number of pre-parsed characters,
// since one was just manually parsed
currentGuess.aufoFilled--
// guess already parsed this part of the string using a RegExp,
// loop to next guess
continue
}
const {
isArray,
isEscaped,
isObject,
isObjectField,
isObjectValue,
isString,
isJSONType
} = currentGuess
/**
* #type {String} The substring _after_ the single-quote.
*/
const followingString = misformattedJSON.slice(stringIndex + 1)
/**
* #type {String} The substring _before_ the single-quote.
*/
const precedingString = misformattedJSON.slice(0, stringIndex)
// change parser state for delimiters
if (character !== "'") {
// keep character
currentGuess.string += character
// set escape sequence, only for inside of a JSON string
if (isString) {
// escape character found,
// if another escape character preceded this one,
// the following character is not part of an escape sequence
// otherwise, the following character is part of an escape sequence
if (character === "\\") currentGuess.isEscaped = !isEscaped
// if isEscaped is false, loop to possible end of string
// if isEscaped is true, ignore escaped single-quote character later
// ignore delimiter characters inside of a JSON string,
// since they do not overlap with escape sequence
continue
}
// set if in an object field and increase object nesting
if (character === "{") {
// set if in an object field
currentGuess.isObjectField = true
currentGuess.isJSONType = "field"
// increase object nesting
currentGuess.isObject++
}
// unset in object field on delimiter and set in object value
else if (character === ":") {
// unset object field on delimiter
currentGuess.isObjectField = false
currentGuess.isJSONType = "object-value"
// set in object value
currentGuess.isObjectValue = true
}
// unset in object value on delimiter and decrease object nesting
else if (character === "}") {
// unset in object value on delimiter
currentGuess.isObjectValue = false
// decrease object nesting
currentGuess.isObject--
currentGuess.isJSONType =
// if this is nested in a root object,
!isArray && isObject
? "object-value"
: // if this is nested in a root array,
!isObject && isArray
? "array"
: // default JSON value type
isArray && isObject
? "value"
: // end of JSON
undefined
}
// increase array nesting
else if (character === "[") {
currentGuess.isArray++
currentGuess.isJSONType = "array"
}
// decrease array nesting
else if (character === "]") {
currentGuess.isArray--
currentGuess.isJSONType =
// if this is nested in a root array,
!isObject && isArray
? "array"
: // if this is nested in a root object,
!isArray && isObject
? "object-value"
: // default JSON value type
isArray && isObject
? "value"
: // end of JSON
undefined
}
// only for objects, not array in object, reset field and unset value
else if (
isObjectValue &&
isJSONType === "object-value" &&
character === ","
) {
// last value ended in last object entry
currentGuess.isObjectValue = false
// new field started in next object entry
currentGuess.isObjectField = true
currentGuess.isJSONType = "field"
}
// only attempt to reformat single-quotes
continue
}
// try all possible solutions by generating
// all possible solutions by brute-force
if (
bruteForce?.({
// make copy to prevent mutations
currentGuess: { ...currentGuess },
followingString,
precedingString,
stringIndex,
characterIndex
})
) {
// create a new guess
guesses.push({
...currentGuess,
// try to make a valid JSON string
// by keeping this single-quote,
string: currentGuess.string + character
// // possibly change these to allow for different heuristics
// isString: currentGuess.isString,
// isEscaped: currentGuess.isEscaped,
// isArray: currentGuess.isArray,
// isObject: currentGuess.isObject
// isObjectField: currentGuess.isObjectField,
// isObjectValue: currentGuess.isObjectValue,
})
// try to make a valid JSON string
// by replacing this single-quote with a double-quote
//! must come after new guess creation
currentGuess.string += '"'
// // possibly change these to allow for different heuristics
// currentGuess.isString = currentGuess.isString
// currentGuess.isEscaped = currentGuess.isEscaped
// currentGuess.isArray = currentGuess.isArray
// currentGuess.isObject = currentGuess.isObject
// currentGuess.isObjectField = currentGuess.isObjectField
// currentGuess.isObjectValue = currentGuess.isObjectValue
}
// use user given heuristics for for how to replace single-quote,
// or change guess parsing state
else if (
heuristic?.({
// give current parser guess and state info to heuristic
currentGuess,
followingString,
precedingString,
stringIndex,
characterIndex
})
) {
/* do nothing... `heuristic` modified object reference directly */
}
// use default heuristics
else if (isString) {
// skip if quote is escaped and clearly inside of a string
if (isEscaped) {
// escape sequence finished
currentGuess.isEscaped = false
// add normal quote character back to string
currentGuess.string += character
// loop to next character
continue
}
/**
* #type {String} JSON object, value, or array ending delimiters.
*/
let possibleDelimiters = ""
// add contextual delimiters for use in heuristics
// delimiter for ending an array,
if (isJSONType === "array") possibleDelimiters = ",\\]"
// delimiter for ending an object,
else if (isJSONType === "object-value") possibleDelimiters = ",}"
// delimiter for ending an object field
else if (isJSONType === "field" || isObjectField)
possibleDelimiters = ":"
// delimiter for ending a value
else if (isJSONType === "value") possibleDelimiters = ",}\\]"
/**
* #type {Boolean} This single-quote is followed by a JSON type
* delimiter, and likely should be a double-quote.
*/
const heuristicDelimiterFollowing =
!!possibleDelimiters &&
new RegExp(String.raw`^\s*[${possibleDelimiters}]`).test(
followingString
)
/**
* #type {RegExpExecArray|null} Finds a different possible solution.
* Starting from the current character, and not the
* `followingString`, so the RegExp includes it in the
* results array, leaving the capturing group with just
* the alternate possible and validated solution.
*/
const heuristicFindValids = new RegExp(
String.raw`([^']*)'\s*[${possibleDelimiters}]`
).exec(misformattedJSON.slice(stringIndex))
/**
* #type {(String|"")} The RegExp validated string capturing group of
* just the characters before the single-quote to
* replace in the RexExp auto-filled guess. Skip if
* empty string, or capturing group is empty string.
*/
const regexpCapturingGroup = heuristicFindValids
? heuristicFindValids[1]
: ""
// both this single-quote and a following single-quote
// have potentially correct resolutions, making the solution ambiguous,
// so create a new guess to try both solutions
if (regexpCapturingGroup) {
// only the following single-quote RegExp heuristic passed,
// which likely means this is a string character that should be kept,
// so use RegEpx auto-fill solution
// this guess will replace the single-quote and end the string
guesses.push({
...currentGuess,
isString: false,
string: currentGuess.string + '"'
})
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: true, // stay string
string: currentGuess.string + character
})
// keep single-quote and use RegExp solution of all characters before
// single-quote from RegExp search that needs to be replaced
currentGuess.string += character + regexpCapturingGroup + '"'
// the length of the entire RegExp guess string
// including ending double-quote
currentGuess.aufoFilled = regexpCapturingGroup.length + 1
// ended string with double-quote
currentGuess.isString = false
}
// only the current single-quote RegExp heuristic passed,
// which likely means this is a misformatted string delimiter
else if (heuristicDelimiterFollowing) {
// WORSE time-complexity mode, custom heuristics but with brute-force
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: false,
string: currentGuess.string + character
})
// replace current misformatted single-quote with a double-quote
currentGuess.string += '"'
// ended string with double-quote
currentGuess.isString = false
}
// neither RegExp heuristic passed,
// which likely means this is a string character that should be kept
else {
// WORSE time-complexity mode, custom heuristics but with brute-force
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: false, // end string
string: currentGuess.string + character
})
// keep single-quote
currentGuess.string += character
}
}
// beginning of a JSON string, reformat single-quote
else {
// replace single-quote with double-quote
currentGuess.string += '"'
// parser now looking for ending single-quote that needs reformatting
currentGuess.isString = true
}
}
}
// try all parsing guesses to see if one works
/**
* #type {Number} Index of guess for debugging information.
*/
let guessIndex = 0
/**
* #type {String[]} Array of every valid JSON string reformatted.
*/
const successes = []
for (const guess of guesses) {
try {
JSON.parse(guess.string)
// reformatting guess parsed as valid JSON!
successes.push(guess.string)
console.log(`successfully parsed #${guessIndex}:`, guess, successes)
} catch (error) {
// // catch JSON parsing error and output for debug
// console.error(`failed: guess #${guessIndex}:`, guess)
}
guessIndex++
}
if (successes.length === 1)
console.log("Parser found definitive answer!\nParsed:", successes[0])
else if (successes.length > 1)
console.log(
"Parser found no definitive answer! Parser found ambiguous answers! Try setting `opts.heuristic` or `opts.bruteForce`, they can get better parsing!\nParsed:",
successes
)
else
console.error(
"Parser Failed to parse any reformatting guess as a valid JSON string! Try setting `heuristic`, `TRY_HARD`, or `opts.bruteForce` (if necessary), they can get better parsing!"
)
// return all valid JSON string guesses
return successes
}
const json1 = `['Bob O'Rielly']`
const json2 = `['Mr. O'McDonald, height 13',1\\"']`
const json3 = `[{'fullName':'Bob O'Rielly','height':'13',5\\"'}]`
const json4 = `[[''''''''''''],[[''''''''],'[]','{}',',',':'],'''''''''''''']`
const json5 = `[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]`
const json6 = `[{'fullName':'Rob O'Rielly','height':'70.5\\"'}]`
const json7 = `[{'fullName':'Dob MacRielly','height':'13',5\\"'}]`
console.log(`ANSWERS 1:`, preParse(json1))
console.log(`ANSWERS 2:`, preParse(json2, undefined, undefined, true))
console.log(`ANSWERS 3:`, preParse(json3, undefined, undefined, true))
console.log(`ANSWER 4:`, preParse(json4))
console.log(`ANSWER 5:`, preParse(json5))
console.log(
`ANSWER 6:`,
preParse(json6, () => true, undefined)
)
console.log(`ANSWER 7:`, preParse(json7, undefined, undefined, true))
The OP is in need of a sanitizing replace task where the used regex needs to explicitly target the single quotes around any property name and any string value. Thus such a regex has to utilize both a positive lookbehind and a positive lookahead. It would look as follows ...
/(?<=[,{:])'|'(?=[:,}])/g
... and its functioning is described at its playground page.
const invalidlyQuotedJSON =
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'Danny O\'Reilly','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
// see ... [https://regex101.com/r/gzCPaS/1]
const regXSingleQuotes = /(?<=[,{:])'|'(?=[:,}])/g;
const validJSON = invalidlyQuotedJSON.replace(regXSingleQuotes, '"');
const parsedData = JSON.parse(validJSON);
console.log({
invalidlyQuotedJSON,
validJSON,
parsedData,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
For environments which do not support lookbehinds like current Safari's one has to change the approach to two different regex patterns each featuring a capturing group for either the leading ... /([,{:])'/g ... or the trailing ... /'([:,}])/g ... character of a to be replaced single quote.
The above provided example code then changes to ...
const invalidlyQuotedJSON =
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'Danny O\'Reilly','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
// see ... [https://regex101.com/r/gzCPaS/2]
const regXTrailingSingleQuote = /([,{:])'/g;
// see ... [https://regex101.com/r/gzCPaS/3]
const regXLeadingSingleQuote = /'([:,}])/g;
const validJSON = invalidlyQuotedJSON
.replace(regXTrailingSingleQuote, '$1"')
.replace(regXLeadingSingleQuote, '"$1');
const parsedData = JSON.parse(validJSON);
console.log({
invalidlyQuotedJSON,
validJSON,
parsedData,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Creating functions in react with d3 to assign colors to value

I'm new to d3, and I had been reading their docs and look at examples but nothing comes close to what I need.
I'm using react and typescript.
I need to create 2 functions, one should look something like this:
mapColor(value, lowerBoundColor, upperBoundColor)
#param value {float} - A decimal float which is between 0 and 1.
#param lowerBoundColor {string} - A hex color code corresponding to a value of 0.
#param upperBoundColor {string} - A hex color code corresponding to a value of 1.
#returns {string} - A hex color code corresponding to the value parameter passed in.
And the secound like this:
linearMap(value, lowerBound, upperBound)
#param value {float} - A decimal float between lowerBound and upperBound.
#param lowerBound {float} - A decimal float which represents the minimum value of the dataset to be clamped.
#param upperBound {float} - A decimal float which represents the maximum value of the dataset to be clamped
#returns {float} - A decimal float between 0 and 1 which corresponds to the value parameter passed in.
This is what I have so far:
import React from 'react'
import * as d3 from 'd3'
var data = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8,0.9,1]
const mapColor = d3.scaleLinear()
.domain([0,1])
.range(["f4a261", "264653"])
How can I create those 2 functions in react to where they can be used as a component in the future.
A valid hex color has to start with a #. So the range should instead be ["#f4a261", "#264653"]. The reusable functions should look like:
function mapColor(value, lowerBoundColor, upperBoundColor) {
return d3.scaleLinear()
.domain([0,1])
.range([lowerBoundColor, upperBoundColor])(value)
}
function linearMap(value, lowerBound, upperBound) {
return d3.scaleLinear()
.domain([lowerBound, upperBound])
.range([0,1])(value)
}
Though I think you should create these using higher-order functions, like so:
function createLinearMap(lowerBound, upperBound) {
return d3.scaleLinear()
.domain([lowerBound, upperBound])
.range([0,1])
}
// Using the HOF
const linearMap = createLinearMap(0, 5)
console.log(linearMap(2)) // 0.4

Why does Infinity passed to interval does not wait forever in Rx.js?

Can someone please explain me why the following snippet immediately outputs digits 0 to 6 ?
Rx.Observable.interval(1/0).take(6).forEach(x => console.log(x));
Output: 0 1 2 3 4 5
You can see from the source code of RxJS that Observable.interval expects (and uses) its input as an integer:
module Rx {
export interface ObservableStatic {
/**
* Returns an observable sequence that produces a value after each period.
*
* #example
* 1 - res = Rx.Observable.interval(1000);
* 2 - res = Rx.Observable.interval(1000, Rx.Scheduler.timeout);
*
* #param {Number} period Period for producing the values in the resulting sequence (specified as an integer denoting milliseconds).
* #param {Scheduler} [scheduler] Scheduler to run the timer on. If not specified, Rx.Scheduler.timeout is used.
* #returns {Observable} An observable sequence that produces a value after each period.
*/
interval(period: number, scheduler?: IScheduler): Observable<number>;
}
}
and since in Javascript
(1/0) === Infinity
and Infinity is zero when turned into an integer:
(Infinity | 0) === 0

JavaScript code explanation for this sample?

Hello everyone! I have completed a couple of brief courses in JavaScript, and I have now moved on to Heads Up: JavaScript, which has been a lot of fun and is helping to cement my learning. I did run into something I didn't understand, though. In the following piece of code, I understand what the program generally does when it executes, but in attempting to trace each step of execution, I realized that I am confounded by the "What/Why/How" of a particular segment. Here's the code for the sample program I'm looking at:
function makePhrases() {
var words1 = ["24/7", "multi-tier", "30,000 foot", "B-to-B", "win-win"];
var words2 = ["empowered", "value-added", "oriented", "focused", "aligned"];
var words3 = ["process", "solution", "tipping-point", "strategy", "vision"];
var rand1 = Math.floor(Math.random() * words1.length);
var rand2 = Math.floor(Math.random() * words2.length);
var rand3 = Math.floor(Math.random() * words3.length);
var phrase = words1[rand1] + " " + words2[rand2] + " " + words3[rand3];
alert(phrase);
}
makePhrases();
This is the segment that has been confusing for me:
var rand1 = Math.floor(Math.random() * words1.length);
var rand2 = Math.floor(Math.random() * words2.length);
var rand3 = Math.floor(Math.random() * words3.length);
I get that it's the part of the code that randomizes which item from each array is chosen to form the new "random phrase", but I don't understand how it's doing so. I also hadn't known previously that Math.random or Math.floor could be applied to strings (must be because they're in an array, which is essentially a number?), or the how/why of using Math.random or Math.floor with strings.
Additionally, why do we need to use .length with this incarnation? What does it do? I appreciate your wisdom here, and taking the time to help someone who's new to coding, and still has so much to learn!
Let's look at the code:
var rand1 = Math.floor(Math.random() * words1.length);
Math.random() returns a number between 0 and 0.999999...
words1 is the list of words to choose from.
words1.length is the size of the list, the number of items, 5 in this case.
Math.random() * words1.length returns a number between 0 and 4.99999...
Finally use Math.floor() to get a whole number between 0 and 4.
This number is then used as an index in words1, so words1[rand1].
So the Math operations are never used on a string, fetching the string in only the last step.
All that's happening is Math.random() is being used as a multiplier against the number of elements in the respective arrays (the '.length' property) to create an index value. It isn't being applied to a string; just as part of an expression to determine an index into a string array.
You want to pick a random element from an array. So you need an index, in other words a random number from 0 to 4 (because your length is 5). Math.random will give you a random number between 0 and 1 (exclusive of 1). So to turn that into a random number between 0 and 4 you need to multiple by the length of 5.
Then, since we need an integer, not a floating point number, we use Math.floor to truncate it to a integer.
Math.random() //Return a random number between 0-1
words1.length() //Return the length of the array
Math.floor() //Return the closest integer less than or equal to a given number.
Now the expressions:
(Math.random() * words1.length)
Will return a random number between 0 and the length of the array. Could be a float, like 3,4 for example:
Math.floor(Math.random() * words1.length)
Will return an integer number between 0 and the length of the string, so you can use it now as the string (behaving like an array) indexer.
Note: Note that the random number is between 0 (inclusive) and 1 (exclusive), that's why is secure to use Math.floor(), to avoid an exception, and that's why is not used Math.ceiling.

Is it possible to check function arguments by google closure linter (gjslint) tool?

For example, I have the following script "test.js":
/**
* Adds two numbers.
* #param {number} a First number.
* #param {number} b First number.
* #return {number} Sum of two numbers.
*/
function add(a, b) {
return a + b;
}
var sum = add(1, 2);
var sum1 = add(1, 2, 4);
var sum2 = add('1', '2');
The function "add" is annotated by jsdoc. It accepts two numbers.
I would like to check the input parameters by gjslint.
gjslint --strict --jslint_error "all" --jsdoc test.js
I expect to see 2 errors for lines where sum1 (3 input parameters) and sum2 (2 string parameters) are calculated. But the tools says "1 files checked, no errors found."
I have not used the Closure Linter, but I believe that is more used for style issues - missing semicolons and such (adherence to http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
The Closure Compiler, however, will give a JSC_WRONG_ARGUMENT_COUNT warning for the use case above.

Categories