How to avoid If and else in spread operator Javascript - javascript

I am quite new to JS. I am trying to simplify my code but its throwing me an syntax error. Is there any way I can avoid multiple codes like not using If and else statement. Below is the code I posted for reference.
the only difference is item.href.replace statement will have "?".
(() => {
const test = 'x=abc';
if (location.search == "") {
[...document.querySelectorAll('a')]
.filter(link => link.href.includes(test))
.forEach(item => (item.href = item.href.replace(`${test}`, location.search)));
} else {
[...document.querySelectorAll('a')]
.filter(link => link.href.includes(test))
.forEach(item => (item.href = item.href.replace(`?${test}`, location.search)));
}
})();

You can simply assign the string you want before the operation.
const test = 'x=abc';
let search = (location.search == "") ? test : `?${test}`;
[...document.querySelectorAll('a')]
.filter(link => link.href.includes(test))
.forEach(item => (item.href = item.href.replace(search, location.search)));

Well, the actual clean solution would be:
[...document.querySelectorAll('a')]
.filter(link => link.href.includes(test))
.forEach(link => link.search = link.search.replace(test, location.search));

We can approach this refactoring through 3 steps:
Move the document.querySelectorAll('a') to the top
Filter the resulting <a> tags that match our criteria through our selector through the ~= selector
Return the results in our function
The resulting code might look something like this:
const replaceQueryParams = function(test) {
const matchingLinks = [...document.querySelectorAll(`a[href~="${test}"]`)];
return matchingLinks.map((link) => link.href = link.href.replace(`?${test}`, window.location.search));
};
We can then use the replaceQueryParams function like so:
const replacedLinkStrings = replaceQueryParams("my-test-string");
if (replacedLinkStrings.length > 0) {
console.log("Do something here");
}

Related

How to update item in array state in react native?

Can someone tell me how I can fix the "updated" issue. I can't update the items dynamically, I can only update manually.
Here is mode code
// Function (async) for rename the playlist and update
const renamePlaylist = async (el) => {
try {
const result = await AsyncStorage.getItem(STORAGE_KEY);
if (result !== null || result !== '') {
/* let filterArrayRenamePlaylist = playList.filter((val) => {
if (val.id !== el ) {
return val;
}
}); */
console.log(el, ' --------- el');
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
// const updateList = [...renamePlayList ];
setUpdate(context, {addToPlayList: null, playList: [...renamePlayList] });
return await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(renamePlayList));
}
setRenamePlaylistModalVisible(false);
} catch (error) {
console.log(error)
}
}
When I try to manually update the playlist title like this:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = 'This works'; // This works
// playList[objIndex].title = el; // This does not works
console.log(el , ' ----------- el 2');
};
});
I searched but couldn't find a solution to my problem.
lets take a closer look to your code and what your code is doing line by line, you made a few logical and code-style mistakes there:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
Issue 1:
Do not write misleading functions, filter function should not modify anything, nobody will expect it to do that, side-effects are bad.Please, consider to separate Filtering and Modifying logic
Issue 2:
Filter function should return a value, true or false. In your case it returns nothing, void, so in the snippet above your renamePlayList will be an empty array, [].
Issue 3: The logical issue itself.
You do not need to seek objIndex, because you have the val element from same array AND array filter function itself do provide index of currently processing element, take a look at the docs please: https://www.w3schools.com/jsref/jsref_filter.asp (Syntax section)
playList[objIndex] is absolutely equal to your val object on first matching val, if you have more elements with same title - your findIndex code will return the first found element only, all the rest elements will be unchanged
Your val.title === el statement and playList[objIndex].title = el; makes no sense due to title is el in any case, like title was el and you are reasigning it to el, if it was "abc" reasigning it to "abc" will not change anything. Your playList[objIndex].title = 'This works'; // This works works exactly because you are setting a different value.
Based on that i would recommend changing your filter function to
playList
.filter((val) => val.title === el)
.forEach((val) => val.title = "new title or whatever you need here, not el btw")
This code will modify elements in playList, not sure if it is expected but if not - just make a deep clone of it before processing.
your setUpdate will need a little change also after that:
setUpdate(context, {addToPlayList: null, playList: [...playlist] });
Please, double check what your code is doing and what are you trying to achieve. My guess is that your top level function needs 2 parameters, like oldTitle and newTitle so it would be
playList
.filter((val) => val.title === oldTitle)
.forEach((val) => val.title = newTitle)

Optimising conditional check in typescript

I have the following JS code were i am displaying different status based on the response key from API. is there any better approach to optimise this code so that i don’t have to check each case with IF, in case if the number of status increases
if (data.shippingStatus) {
let shippingStatus = data.shippingStatus.toString();
if (shippingStatus === "AWAITING_SHIPMENT") {
shippingStatus = "Awaiting Shipment";
} else if (shippingStatus === "SHIPPED") {
shippingStatus = "Shipped";
} else if (shippingStatus === "DELIVERED") {
shippingStatus = "Delivered";
} else if (shippingStatus === "CANCELLED") {
shippingStatus = "Cancelled";
}
resData.push(setData(data.shippingStatus ? shippingStatus : ""));
}
Try object mapper:
const statusMapper: {[key:string]: string} = {
AWAITING_SHIPMENT: "Awaiting Shipment",
SHIPPED: "Shipped",
DELIVERED: "Delivered",
CANCELLED: "Cancelled"
};
if (data.shippingStatus) {
let shippingStatus = data.shippingStatus.toString();
resData.push(setData(data.shippingStatus ? statusMapper[shippingStatus] : ""));
}
EDIT: Added type to the mapper
There are different approaches to your question. If you're just looking for a solid solution for the current problem aka mapping values, you can either create an object mapper as the other answers suggest or just a simple function that formats your string e.g.:
var text = "AWAITING_SHIPMENT";
text = text.toLowerCase()
.split('_')
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
.join(' ');
console.log(text);
But if you're looking into the subject in a broader sense, you can use dynamic dispatch via polymorphism. This is an example that uses polymorphism to change behavior based on a type.
How my implementation works, it splits the status by _ and capitalize it, and finally return a new status, as required
// Dummy Data
const data = {
shippingStatus:"AWAITING_SHIPMENT"
}
// New Proposal
if(data.shippingStatus){
const { shippingStatus } = data;
const status =
shippingStatus.split('_')
.map(string => string.charAt(0).toUpperCase() + string.slice(1).toLowerCase())
.join(" ");
console.log(status)
// no need to check for data.shippingStatus twice
// while you're still within if condition, just do the following :
// resData.push(setDate(status))
}
Have map of possible statuses:
let statuses = {
'AWAITING_SHIPMENT': 'Awaiting Shipment',
'SHIPPED': 'Shipped',
...
};
resData.push(setData(data.shippingStatus ? statuses[shippingStatus] : ""));

Function declared in a loop contains unsafe references to variable(s)

My code checks if a user is available. See snippet below:
const users = ['user1', 'user2', 'user3', 'user4']
const usersToAdd = 2
const getRandomWorker = (userArray) => {
return userArray[Math.floor(userArray.length * Math.random())]
}
const availableUsers = []
for (let i = 0; i < usersToAdd; i += i) {
let randomWorker = getRandomWorker(users)
let didAddWorker = false
while (!didAddWorker) {
if (checkIfUserAvailable(randomWorker)) {
availableUsers.push(randomWorker)
users = users.filter((user) => user !== randomWorker)
didAddWorker = true
} else if (!users.length) {
didAddWorker = true
} else {
users = users.filter((user) => user !== randomWorker)
}
}
}
My only problem is that it contains unsafe references to variables(s) because I get the following error:
Function declared in a loop contains unsafe references to variable(s) randomWorker.
I've searched around and fiddled with my code but I can't get rid of the error. I don't know where to look anymore.
As T.J Crowder suggested I had to make a function outside of the loop:
const filterOut = (array, target) => array.filter(element => element !== target);
That fixed it. A bit strange but I can continue know. Thanks!

Unable To Pass Objects/Arrays in IPCRenderer, An object could not be cloned EventEmitter.i.send.i.send

I am unable to pass any object or arrays to IPCRenderer.
I am getting error when passing an object or array through ipcs, I have even tried to send by converting to string using JSON.stringify but it converts it into empty object string.
I have tried passing a fileList, an array of object & even an object nothing passes. only string or handwritten objects are working.
I've read that it uses Structured Clone Algorithm and fileList & Array is allowed by this algorithm
ERROR:
electron/js2c/renderer_init.js:74 Uncaught Error: An object could not be cloned.
at EventEmitter.i.send.i.send (electron/js2c/renderer_init.js:74)
at HTMLButtonElement.compressNow (ImageHandling.js:190)
I have tried many possible solutions but nothing worked
code:
const compressNow = () => {
ipcRenderer.send("image:compress", filess). ///This is the error.
// filess is a variable containing an array of selected files from an HTML input.
}
Now i have tried to send filess as JSON.stringify, i tried to send it as an object but nothing works unless i manually write a dummy object or string.
Here's My Github Repo for this project
Files With ErrorJ:-
ImageHandling.js
const fs = window.require('fs');
const {ipcRenderer} = require("electron")
const SELECT = (target) => document.querySelector(`${target}`)
var filess = []
const imgUploadInput = SELECT("#imgUploadInput")
const warning = SELECT("#warning")
const setImgBase64 = (imgEl, file) => {
const ReadAbleFile = fs.readFileSync(file.path).toString('base64')
let src = "data:image/png;base64," + ReadAbleFile
imgEl.setAttribute("src", src)
// El.src=src
// console.log(`FIXED IMAGE # ${imgEl} `,ReadAbleFile)
}
const renderImages = () => {
const files = filess && Array.from(filess)
const defaultImg = SELECT("#defaultImg")
const addImgBtn = SELECT("#addImgBtn")
imgUploadInput.disabled = true;
let numOfFiles = files.length
if (numOfFiles < 1) {
SELECT("#compressContainer").style.visibility = "hidden"
} else {
SELECT("#compressContainer").style.visibility = "visible"
}
if (numOfFiles > 49) {
warning.innerHTML = `<b style="font-weight:bold; color:red;">WARNING:</b><br/>
<span style="padding:10px;text-align:left">
Your processor/computer may not be able to process ${numOfFiles} Images at once, We recommend selecting less than 50 Images at once for better performance.
</span>
`;
}
addImgBtn.innerHTML = `LOADING.....`
if (defaultImg && numOfFiles > 0)
defaultImg.remove();
setTimeout(() => {
if (files && numOfFiles > 0) {
let displayImages = SELECT("#displayImages")
displayImages.innerHTML = ""
files ?. forEach((file, i) => {
let divEl = document.createElement("div")
let imgEl = document.createElement("img")
imgEl.src = file.path
imgEl.id = `PNG_${i}_${
btoa(file.name)
}`
divEl.className = "displayedImg"
imgEl.setAttribute("onclick", `document.getElementById('ImageView').src=this.src`)
const a = document.createElement("a")
a.appendChild(imgEl)
a.setAttribute("href", `#ViewImage`)
a.className = "perfundo__link"
divEl.appendChild(a)
divEl.className = "displayedImg perfundo"
displayImages.appendChild(divEl)
if (i == files.length - 1) {
warning.innerHTML = "";
updateNumOfImages();
}
imgEl.onerror = () => setImgBase64(imgEl, file) // converting to base64 only on error, this make performance better and help us avoid freezes. (before this i was converting all images to base64 wither errored or not that was making computer freez)
})
addImgBtn.innerHTML = "+ Add MORE"
imgUploadInput.disabled = false
findDuplicate()
}
}, 0);
}
const hasDuplicate=()=>{
let FileNames = [... filess.map(f => f.name)]
let duplicateFiles = filess.filter((file, i) => FileNames.indexOf(file.name) !== i)
return {FileNames,duplicateFiles,FilesLength:duplicateFiles.length}
}
const findDuplicate = (forceAlert = false) => {
if (filess && filess.length) {
let {FileNames} = hasDuplicate()
let {duplicateFiles} = hasDuplicate()
if (duplicateFiles.length) { // alert(``)
let countFiles = duplicateFiles.length
let fileStr = countFiles > 1 ? "files" : "file"
console.log("result from removeDup=> ", filess, " \n dupfilename=> ", FileNames, " \n dupfiles=> ", duplicateFiles)
let shouldNotAsk = localStorage.getItem("NeverAsk")
let msg = `You've selected ${
countFiles > 1 ? countFiles : "a"
} duplicate ${fileStr}`
let duplInner = `<span style='color:red'>
<b>WARNING</b>
<p style="margin:0px;line-height:1"> ${msg} . <button onClick="findDuplicate(true)" type="button" class="btn btn-danger btn-rounded btn-sm">REMOVE DUPLICATE</button></p>
</span>`
if (! shouldNotAsk || forceAlert) {
swal("DUPLICATE FILES DETECTED", `${msg} , Would you like to un-select duplicate ${fileStr} having same name?`, {
icon: 'warning',
dangerMode: true,
buttons: {
cancel: true,
...forceAlert ? {} : {
never: "Never Ask"
},
confirm: "Yes !"
}
}).then((Yes) => {
if (Yes == "never") {
localStorage.setItem("NeverAsk", true)
warning.innerHTML=duplInner
} else if (Yes) {
removeDuplicates()
}
})
} else {
warning.innerHTML=duplInner
}
}
}
}
const removeDuplicates = (showAlert=true) => {
let {FileNames} = hasDuplicate()
let {duplicateFiles} = hasDuplicate()
let duplicateFileNames = duplicateFiles.map(f => f.name)
let uniqueFiles = filess.filter((file) => ! duplicateFileNames.includes(file.name))
filess = [
... uniqueFiles,
... duplicateFiles
]
console.log("result from removeDup=> ", filess, " \n filename=> ", FileNames, " \n dupfiles=> ", duplicateFiles, "\n unique fil=> ", uniqueFiles)
renderImages()
if(showAlert){
swal("DONE", "Removed Duplicate Files ", {icon: 'success'}).then(() =>{
renderImages()
setTimeout(() => {
let hasDuplicateFiles = hasDuplicate().FilesLength
if(hasDuplicate){//Re-check if any duplicate files left after the current removal process.
removeDuplicates(false) //Re-run the function to remove remaining. false will make sure that this alert does not show and the loop does not continue.
}
renderImages()
}, 10);
})
}
}
const updateNumOfImages = () => {
warning.innerHTML = `
<span style="text-align:left; color:green">
Selected ${
filess.length
} Image(s)
</span>
`;
}
const compressNow = () => {
ipcRenderer.send("image:compress", filess)
// alert("WOW")
}
CompressBtn.addEventListener("click", compressNow)
imgUploadInput.addEventListener("change", (e) => {
let SelectedFiles = e.target.files
if (SelectedFiles && SelectedFiles.length) {
filess = [
... filess,
... SelectedFiles
]
renderImages()
}
})
// SELECT("#imgUploadInput").addEventListener("drop",(e)=>console.log("DROP=> ",e))
UPDATE:-
I REPLACED THIS:
const compressNow = () => {
ipcRenderer.send("image:compress",filess)
}
INTO THIS:-
const compressNow = () => {
filess.forEach(file => {
ipcRenderer.send("image:compress",file.path )
});
}
Now here i am sending the files one by one via forEach, actually its sending string "file path" so thats how its working i am still confused why do i have to do this? why can't i send whole fileList i assume that this loop method is a bad practice because it will consume more CPU its one additional loop however it won't be necessary if i am able to send the whole array.
See Behavior Changed: Sending non-JS objects over IPC now throws an exception. DOM objects etc. are not serializable. Electron 9.0 (and newer) throws "object could not be cloned" error when unserializable objects are sent.
In your code, File and FileList are DOM objects.
If you want to avoid using forEach, try this code:
const compressNow = () => {
const paths = filess.map(f => f.path);
ipcRenderer.send("image:compress", paths);
}
Can refer to electron github issue tracker for this issue (already closed)
Error: An object could not be cloned #26338
Docs for ipcRenderer.send(channel, ...args)
This issue mainly comes when we have non-cloneable values like function within an object in data we are sending via IPC, to avoid that we can use JSON.stringify() before sending and JSON.parse() later on receiving end, but doing so will cause to lose some of the values eg:
const obj = {
x :10,
foo : ()=>{
console.log('This is non-cloneable value')
}
}
console.log(JSON.stringify(obj))
output:{"x":10}
Instead of sending the images save them in fs and send the path
The simplest thing that could possibly work is to use lodash cloneDeep()
ipcMain.handle('stuffgetList', async () => {
return _.cloneDeep(await stuffStore.getList())
})
in the windows JSON.stringify()
in the main.js JSON.parse()
Remove :compress from. .send method and try

what does "event =&gt" does in RxJS?

I came across this code below but I don't understand why we are doing event is equal and greater than and then console.log
I would really appreciate if someone explains it
const node = document.querySelector('input[type=text]');
const input$ = Rx.Observable.fromEvent(node, 'input');
input$.subscribe({
next: event => console.log(`You just typed ${event.target.value}!`),
error: err => console.log(`Oops... ${err}`),
complete: () => console.log(`Complete!`),
});
const input$ = Rx.Observable.fromEvent(node, 'input')
.map(event => event.target.value)
.filter(value => value.length >= 2)
.subscribe(value => {
// use the `value`
});
This looks like Javascript code that's been passed through an HTML sanitizer.
The original code using arrow functions should be as follows:
const node = document.querySelector('input[type=text]');
const input$ = Rx.Observable.fromEvent(node, 'input');
input$.subscribe({
next: event => console.log(`You just typed ${event.target.value}!`),
error: err => console.log(`Oops... ${err}`),
complete: () => console.log(`Complete!`),
});
const input$ = Rx.Observable.fromEvent(node, 'input')
.map(event => event.target.value)
.filter(value => value.length >= 2)
.subscribe(value => {
// use the `value`
});
This is invalid JavaScript. =&gt should be => and what you're seeing is simply ES6's arrow functions.
This is a display bug in the page you're seeing. It probably stems from the fact that < and > in HTML text should be transformed into < and > so as not to cause parse errors with the same characters when they serve as tag opening and closing (as in <div>).

Categories