Why isn't my nullish coalescing operator (??) working as intended? - javascript

Here are the relevant sections of my code:
HTML (excerpt)
<label for="name">File delimiter (optional, max 1 character):</label>
<input type="text" id="delimiter" name="delimiter" required
minlength="0" maxlength="1" size="1"><br>
JS (excerpt)
async function getRelease(idFiltered) {
return fetch(`https://api.***.com/releases/${idFiltered}`, {
headers: {
'User-Agent': '***/0.1',
'Authorization': `*** key=${KEY}, secret=${SECRET}`,
},
}).then(response => response.json())
.then(data => {
if (data.message === 'Release not found.') {
return { error: `Release with ID ${idFiltered} does not exist` };
} else {
const { country = 'Unknown', genres = [], styles = [], year = 'Unknown' } = data;
const artists = data.artists?.map?.(artist => artist.name);
const barcode = data.identifiers.filter(id => id.type === 'Barcode').map(barcode => barcode.value);
const catno = data.labels.map(catno => catno.catno);
const descriptions = data.formats.map(descriptions => descriptions.descriptions);
const format = data.formats.map(format => format.name);
const labels = data.labels.map(label => label.name);
const qty = data.formats.map(format => format.qty);
const tracklist = data.tracklist.map(track => track.title);
const formattedLabels = labels.map(label => label.name);
const delimiter = document.getElementById("delimiter").value ?? "|";
const formattedBarcode = barcode.join(delimiter);
const formattedCatNo = catno.join(delimiter);
const formattedGenres = genres.join(delimiter);
const formattedStyles = styles.join(delimiter);
const formattedTracklist = tracklist.join(delimiter);
const preformattedDescriptions = descriptions.toString()
.replace('"', '""').replace(/,/g, ', ');
const formattedDescriptions = '"' + preformattedDescriptions + '"';
console.log(data);
return [idFiltered,
artists,
format,
qty,
formattedDescriptions,
formattedLabels,
formattedCatNo,
country,
year,
formattedGenres,
formattedStyles,
formattedBarcode,
formattedTracklist
];
}
});
}
When I manually input the delimiter | in the text box on my HTML page, I get the correct output back, for instance...
5,Datacide,CD,1,"Album, Limited Edition",,,RI 026|RI026,Germany,1995,Electronic,Abstract|Ambient|Downtempo,,Flashback Signal|Flowerhead|Deep Chair|So Much Light|Sixties Out Of Tune
but when I don't do that, it is missing!
5,Datacide,CD,1,"Album, Limited Edition",,,RI 026RI026,Germany,1995,Electronic,AbstractAmbientDowntempo,,Flashback SignalFlowerheadDeep ChairSo Much LightSixties Out Of Tune
I guess the missing delimiter is evaluating to neither null nor undefined for some reason, because I read that the nullish coalescing operator only returns the second operand "when the first one evaluates to either null or undefined (but no other falsy values)" source. So this seems to work instead:
const delimiter = document.getElementById("delimiter").value || "|";
Can anyone tell me why ?? isn't working for me, and is it OK to use || instead? Thanks.
Edit: I tried switching to ||, but I'm finding that the delimiter is only inserted correctly when I serve my app from my local machine, and not when I serve it from GitHub pages. So it seems this issue is specific to GitHub pages.

Your delimiter could be an empty string, which is falsy, but not nullish. That’s why the logical or works. An empty string is logical false, and so are undefined and null. The || operator will return the right-hand value in more cases than the ?? operator.

According to the docs:
The value (of a text input field) may be an empty string ("").
That's why you cannot rely on the nullish coalescing operator, it only works for null and undefined, while the logical || operator will check if the value is falsy, which is also the case for empty string.

Related

Convert Comma-separated Values Into Tags React Native with TypeScript

I want to convert comma-separated values into tags. In fact I already have a good part done, but I have a problem. the tags come "added" with the previous ones. If I type in the input "ex1,ex2", the tags "ex1" and "ex1,ex2" will be created, but the tags that should be created are "ex1" and "ex2". look: .
this is my input:
<Input
onChangeText={(text: string) => handleTagChange(text)}
onKeyPress={handleTagKey}
value={inputTag}
/>
and here is the functions:
const handleTagChange = (text: string) => {setInputTag(text);}
const handleTagKey = (event: NativeSyntheticEvent<TextInputKeyPressEventData>) =>
{
const { key } = event.nativeEvent;
const trimmedInput = inputTag.trim();
if(key === ',' && trimmedInput.length && !tags.includes(trimmedInput)) {
event.preventDefault();
setTags((prev) => [...prev, trimmedInput]);
}
}
I thought it would be enough to set an empty string in the input value after setTags (setInputTag("")), but apparently it's not that simple.
would this work for you?
setTags(trimmedInput.split(","));
you must first remove the last comma

How to find similarities between a text string and an array?

I have a string array, which I want to compare with a text string, if it finds similarities I want to be able to mark in black the similarity found within the string
example:
context.body: 'G-22-6-04136 - PatientName1'
newBody: ['G-22-6-04136 - PatientName1' , 'G-22-6-04137 - PatientName2']
When finding the similarity between the two the result would be something like this
newBody: [**'G-22-6-04136 - PatientName1'** , 'G-22-6-04137 - PatientName2']
How could I do it? Is this possible to do? In advance thank you very much for the help
const totalSize: number = this.getSizeFromAttachments(attachments);
const chunkSplit = Math.floor(isNaN(totalSize) ? 1 : totalSize / this.LIMIT_ATTACHMENTS) + 1;
const attachmentsChunk: any[][] = _.chunk(attachments, chunkSplit);
if ((totalSize > this.LIMIT_ATTACHMENTS) && attachmentsChunk?.length >= 1) {
const result = attachment.map(element => this.getCantidad.find(y => element.content === y.content))
const aux = this.namePatient
const ans = []
result.forEach(ele => {
const expected_key = ele["correlative_soli"];
if (aux[expected_key]) {
const newItem = { ...ele };
newItem["name_Patient"] = aux[expected_key]
newItem["fileName"] = `${expected_key}${aux[expected_key] ? ' - ' + aux[expected_key] : null}\n`.replace(/\n/g, '<br />')
ans.push(newItem)
}
});
let newBody: any;
const resultFilter = attachment.map(element => element.content);
const newArr = [];
ans.filter(element => {
if (resultFilter.includes(element.content)) {
newArr.push({
fileNameC: element.fileName
})
}
})
newBody = `• ${newArr.map(element => element.fileNameC)}`;
const date = this.cleanDateSolic;
const amoung= attachmentsChunk?.length;
const getCurrent = `(${index}/${attachmentsChunk?.length})`
const header = `${this.cleanDateSolic} pront ${attachmentsChunk?.length} mail. This is the (${index}/${attachmentsChunk?.length})`;
console.log('body', context.body);
// context.body is the body I receive of frontend like a string
const newContext = {
newDate: date,
amoung: amoung,
getCurrent: getCurrent,
newBody: newBody,
...context
}
return this.prepareEmail({
to: to,
subject: ` ${subject} (Correo ${index}/${attachmentsChunk?.length})`,
template: template,
context: newContext,
}, attachment);
}
I have a string array, which I want to compare with a text string, if it finds similarities I want to be able to mark in black the similarity found within the string
First of all the text will be in black by default, I think you are talking about to make that bold if matched. But again it depends - If you want to render that in HTML then instead of ** you can use <strong>.
Live Demo :
const textString = 'G-22-6-04136 - PatientName1';
let arr = ['G-22-6-04136 - PatientName1', 'G-22-6-04137 - PatientName2'];
const res = arr.map(str => str === textString ? `<strong>${str}<strong>` : str);
document.getElementById('result').innerHTML = res[0];
<div id="result">
</div>
Based on your example, it looks like the string needs to be an exact match, is that correct?
Also, "mark it in bold" is going to depend on what you're using to render this, but if you just want to wrap the string in ** or <b> or whatever, you could do something like this:
newBody.map(str => str === context.body ? `**${str}**` : str);
You can just see if string exists in the array by doing
if arr.includes(desiredStr) //make text bold

print value to screen according to condition

I am getting the "address" value from the backend. However, the incoming value is all uppercase. I just want the first letters to be uppercase. I achieved this. But I get an error when "address" value does not come.
const upperCaseFirstLetter = (string) =>
`${string.slice(0, 1).toUpperCase()}${string.slice(1)}`;
const lowerCaseAllWordsExceptFirstLetters = (string) =>
string.replaceAll(/\S*/g, (word) => `${word.slice(0, 1)}${word.slice(1).toLowerCase()}`
);
let address = '';
address = upperCaseFirstLetter(lowerCaseAllWordsExceptFirstLetters(props.policy.assets.address));
html
<div className="policycard__subinfo">
{props.policy.assets.address ? (t('policy.policy-cards.address') + address) : null}
</div>
The error in the console is as follows; Cannot read properties of undefined (reading 'replaceAll').
There is probably a better way, but any time I run into an error like that I like to just give it a default value:
const lowerCaseAllWordsExceptFirstLetters = (string) => {
return (string || '').replaceAll(/\S*/g, (word) => `${word.slice(0, 1)}${word.slice(1).toLowerCase()}`
}
so that if string is undefined, it instead is treated as an empty string and you avoid the error.

How can I map a concatenated value for this state, without it being passed as a string?

I'm new to Javascript.
I'm building this drumpad and I want to be able to switch between different soundpacks.
The sounds are imported from a separate file and the state is written like this:
import * as Sample from '../audiofiles/soundfiles'
const drumpadData = [
{
// other stuff
soundfile: Sample.sound1a
},
If I want to load a different soundpack, I have to change the state so the last letter (a,b,c) gets changed, so instead of Sample.sound1a, it would have to be Sample.sound1b. this is the function i wrote (on App.js):
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = "Sample.sound" + item.id + choice
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
It works, as in the value gets changed, but instead of react interpreting that and finding the correct import, the value of soundfile just stays as a string like "Sample.soundb1", so I get a load of media resource errors.
https://aquiab.github.io/drumpad/ heres the website, you can check the console to see the error, you have to load a different soundpack to reproduce the error.
https://github.com/aquiab/drumpad and here are the files:
I've thought of some ways of cheesing it, but I want the code to stay as clean as I can make it be.
Well that's because it is in fact a string. When you do:
"Sample.Sound" + item.id + choice you are doing type coersion. In JavaScript, that means you are converting the value of all data-types so that they share a common one. In this case your output resolves into a string. This will not be effective in finding the right sound in your dictionary.
Instead, what you need is bracket notation: Object[property]
Within the brackets we can define logic to identify the designated key belonging to the Object.
For example: Sample["sound" + item.id + choice] would evaluate to Sample["sound1b"] which is the same as Sample.sound1b
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
item.soundfile = Sample["sound" + item.id + choice]
return item
})
return {
data: updatedData
}
})
}
I can think of two approaches here.
import Sample in App.jsx. and update sound file.
import * as Sample from '../audiofiles/soundfiles'
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = Sample[`sound${item.id}${choice}`]
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
You should save mapping of files in other object and update mapping and use mapping in drumpadData soundfile key.
Your problem comes from this line :
let newSoundfile = "Sample.sound" + item.id + choice
Here you are concatening your values item.id and choice with a string, so the result is a string and Sample is not interpreted as your imported object.
What you need is to wright something like
const sampleItem = "sound" + item.id + choice
let newSoundfile = Sample[sampleItem]
When you access an object property with the notation myObject[something], what's inside the bracket get interpreted. So in my example sample (which is a string because I concatenated a string "sound" with the variables) will be replaced with its string value (ex: "sound1a"), and newSoundFile will have as value the result of Sample["sound1a"].
I hope it make sens.

formatting number in input react

I have made an Input component. If it is a number I want to format it correctly, like a currency. I.e. 4000 would be 4,000.
Here is a codesandbox.
I am having issues with displaying and updating this.
<Input initialValue={'400000000'} isNumber={true} />
My Input component looks like this.
type Props = {
initialValue?: string;
isNumber?: boolean;
};
const Input = ({ initialValue = '', isNumber }: Props) => {
const [value, updateValue] = useState(initialValue);
const update = (val: any) => {
if (isNumber) {
const x = Number(val);
updateValue(x.toLocaleString());
} else {
updateValue(val);
}
};
return (
<StyledInput
type="text"
value={value}
onChange={e => update(e.target.value)}
/>
);
};
I am seeing an error NaN in my input component. Anyone have any ideas?
Javascript has a number formatter (part of the Internationalization API).
// Quick Solution With Intl.NumberFormat
const update = (val: any) => {
var formatter = new Intl.NumberFormat("en-US"); // Intl language tag,
updateValue(formatter.format(val.replace(/,/g, ""))); //Remove ',' to format number again
};
Code Snippet:
// Intl.NumberFormat With React State Update
var currentVal = 0;
...
const update = (event: any) => {
/**
https://stackoverflow.com/questions/35535688/stop-cursor-jumping-when-formatting-number-in-react
https://github.com/facebook/react/issues/955
*/
const caret = event.target.selectionStart
const element = event.target
window.requestAnimationFrame(() => {
element.selectionStart = caret
element.selectionEnd = caret
})
// -- Stop cursor jumping when formatting number in React
var val = event.target.value.replace(/(\..*)\./g, '$1') //Replace Multiple Dot(.)
var x = Number(val.replace(/,/g, ""));
if (currentVal != x) {
var formatter = new Intl.NumberFormat("en-US", { minimumFractionDigits:2});
currentVal = formatter.format(x);
updateValue(currentVal);
}else{
updateValue(val);
}
};
return (<input type="text" value={value} onChange={e => update(e)} />);
Note : Code Snippet gives you an idea to format numbers, You need to handle few more use-cases for production.
Also check the react-number-format, Which may suit for your application.
Reference :
Intl.NumberFormat vs Number.prototype.toLocaleString
How can I format numbers as dollars currency string in
JavaScript?
Intl.NumberFormat | MDN
The problem is in
const x = Number(val);
when you evaluate Number("32,423,343"), a string including commas Js will throw an error...
The correct way would be sending the number without commas.. Number("32432343")
To solve it you can add this line to remove the commas, before evaluating to a Number..
val = val.replace(/,/g, '');
https://codesandbox.io/s/r0vm8nxvjo

Categories

Resources