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; }
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
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.