Is there a better way to achieve this? - javascript

I am using React. On click of a button, the following function is executed:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const updatedItem = updatedData.filter((ele) => ele.id === idValue)[0];
updatedItem.completed = true;
const newData = updatedData.filter((ele) => ele !== updatedItem);
newData.unshift(updatedItem);
return newData;
});
};
My data is an array of objects like this:
[{userId: 1, id: 2, title: "task 1", completed: true}, .....].
Basically I want to move the updated item to the start of the array. Is there any better solution for this?

updatedItem should not be mutated. And this string const newData = updatedData.filter((ele) => ele !== updatedItem); is not fine. You can do it like this :
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetItem = prevData.find((ele) => ele.id === idValue);
const updatedItem = { ...targetItem, completed: true };
const filteredData = prevData.filter((ele) => ele.id !== idValue);
return [updatedItem, ...filteredData];
});
};

Even better to reducing an extra filter:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetIndex = prevData.findIndex((ele) => ele.id === idValue);
return [{ ...prevData[targetIndex], completed: true }].concat(prevData.slice(0, targetIndex + 1)) .concat(
prevData.slice(targetIndex + 1)
)
});
};

First find index of updated element using Array.findIndex(), then remove the same element using Array.splice() and add it to front of the array.
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const index = updatedData.findIndex(obj => obj.id === idValue);
const [updatedItem] = updatedData.splice(index, 1);
updatedItem.completed = true;
updatedData.unshift(updatedItem);
return updatedData;
});
};

The simplest one with only one forEach.
const completeTaskHandler = idValue => {
setData(prevData => {
let updatedItem = {}, newData = [];
prevData.forEach((ele) => {
if (ele.id === idValue) {
updatedItem = ele;
updatedItem.completed = true;
} else {
newData.push(ele);
}
});
newData.unshift(updatedItem);
return newData;
});
};

Related

Run async/await function inside a reduce Javascript [duplicate]

This question already has answers here:
JavaScript array .reduce with async/await
(11 answers)
Closed 6 months ago.
I need to fetch values from another API using the guid inside this particular array, then group them together (hence I used reduce Javascript in this case)
However, I could not get those values sumEstimatedHours and sumWorkedHours as expected. Can someone suggest a method please?
export const groupProjectsByPM = (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = listOfProjects?.reduce(
(
group,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
here is an example of async/await inside reduce:
let's assume that we have an array of numbers
const arrayOfNumbers = [2,4,5,7,6,1];
We are going to sum them using reduce function:
const sumReducer = async () => {
const sum = await arrayOfNumbers.reduce(async (promisedSum, num) => {
const sumAcc = await promisedSum
// any promised function can be called here..
return sumAcc + num
}, 0)
console.log(sum)
}
So the trick is to remember to await the accumulator inside the reduce function
export const groupProjectsByPM = async (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = await listOfProjects?.reduce(
async (
promisedGroup,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
//accumulator in your case is group
const group = await promisedGroup;
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
Best of luck ...

how to run useEffect only twice

Here is My useEffect is going in Infinite loop, becouse checkimage value is changing becouse the value is assigned in fetch(), so anyone know how to solve it. I want to get varient data with image but I can't get it in first time.
help me if you can
Thank You
useEffect(() => {
fetch({ pagination });
}, [checkimage]);
const fetch = async (params = {}) => {
if (type == 'product') {
dispatch(await ProductService.getProduct(productId))
.then((res) => {
let variantsdatas = getImageArns(res.data.variants);
getImages(variantsdatas);
let record = [];
record.push(res.data)
setVarientsData(record)
})
.catch((err) => {});
} else {
dispatch(await ProductService.getProducts())
.then((res) => {
console.info({ 'res.data': res.data });
setVarientsData(res.data.products);
setPagination({
...params.pagination,
total: res.total_count,
});
})
.catch((err) => {});
}
};
const getImageArns = (variantsdatas) => {
const variantImageArns = [];
variantsdatas.forEach((variant, index) => {
variant[index] = variant.variantId;
if (variant.variantImagesListResponseDto.images.length > 0) {
let variantImageObj = {
variantId: variant.variantId,
arnUrl: variant.variantImagesListResponseDto.images[0].docUrl,
};
variantImageArns.push(variantImageObj);
}
});
// console.info('id', variantImageArns);
return variantImageArns;
};
const getImages = async (variantsdatas) => {
const images = [];
dispatch(await ProductVariantService.getImage(variantsdatas))
.then((res) => {
console.info(res.data.fileResponseDtoList);
let presignedURLs = {};
res.data.fileResponseDtoList.map(
(i) => (
(presignedURLs = {
variantId: i.variantId,
arnUrl: i.presignedURL,
}),
console.info(presignedURLs),
images.push(presignedURLs)
)
);
setcheckimage(images);
})
.catch((err) => {
console.info('Get Error District...');
});
};
var img = 'img';
const setVarientsData = (products) => {
let varients_array = [];
if (products.length > 0) {
products.forEach((product) => {
if (product.variants.length > 0) {
let product_varients = product.variants;
product_varients.forEach((varient) => {
for (var f = 0; f < checkimage.length; f++) {
if(checkimage[f].variantId == varient.variantId){
img = checkimage[f].arnUrl;
f = checkimage.length
}
else{
img = 'img2';
}
}
varients_array.push({
image: img,
variantId: varient.variantId,
productVariantName: varient.variantName,
productName: product.productName,
brand: '-',
sellerSku: varient.productVariantCode,
status: product.status,
category: product.subCategoryInfo.categoryInfo.categoryName,
subCategoryName: product.subCategoryInfo.subCategoryName,
state: '-',
market: '-',
mrp: varient.price.amount + ' ' + varient.price.currency,
sellingPrice: '-',
manufacturer_product_variant_code:
varient.manufacturerProductVariantCode,
product_varient_code: varient.azProductVariantLongCode,
hsnCode: varient.hsnCode,
});
});
}
});
}
setVarients(varients_array);
console.info('varients_array ====>>', {varients_array})
};
I think that if I stop to run blow code getImage function then I can get my result
am I right?
But I tried It too but is also not happening properly.
a quick and dirty fix could be to work with a counter.
and only run the fetch in the useEffect, when counter is 0.
have you tried that?

How to remove mutation on this code Javascript?

I managed to write code which does what I need, but I want to make it clean and remove mutation of realtiveGroups.push() but don't know how to achieve it.
How to remove mutation from this code?
export interface OfferCategory {
id: string;
name: string;
}
export interface OfferGroup {
groupId: string;
dependsOn: OfferCategory;
name: string;
rule: Rule;
products: PosProducts[];
}
function relativesFromSubscription(groups: OfferGroup[], dependingGroups: OfferGroup[]): OfferGroup[] {
const relativeGroups: OfferGroup[] = [];
groups.forEach(group => {
if (dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId)) {
relativeGroups.push(group);
}
if (relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)) {
relativeGroups.push(group);
}
});
return relativeGroups;
}
Instead of doing everything in one cycle try dividing it into a few:
function relativesFromSubscription(groups: OfferGroup[], dependingGroups: OfferGroup[]): OfferGroup[] {
const groups1 = groups.filter(group => dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId));
const groups2 = groups.filter(group => groups1.some(relGroup=> group?.dependsOn?.id === relGroup.groupId));
return [...groups1, ...groups2];
}
Using your code and Array.filter
const relativeGroups: OfferGroup[] = groups.filter(group => {
return dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId) || relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)
});
Or if you want the code to be more readable you can add descriptive variables:
const relativeGroups: OfferGroup[] = groups.filter(group => {
const hasDependingGroups = dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId);
const hasRelativeGroups = relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)
return hasDependingGroups || hasRelativeGroups
});

Vanilla JavaScript search - how to add multiple fields?

This function is searching from a Json data the field "title".
Please, how can I modify this to include multiple fields, like: "tags", "author" etc.? Thanks!
document.addEventListener('DOMContentLoaded', function(event) {
const search = document.getElementById('search');
const results = document.getElementById('results');
let data = [];
let search_term = '';
fetch('/search.json')
.then(response => response.json())
.then(data_server => {
data = data_server;
});
search.addEventListener('input', event => {
search_term = event.target.value.toLowerCase();
showList();
});
const showList = () => {
results.innerHTML = '';
if (search_term.length <= 0) return;
const match = new RegExp(`${search_term}`, 'gi');
let result = data.filter(name => match.test(name.title));
if (result.length == 0) {
const li = document.createElement('li');
li.innerHTML = `No results found 😢`;
results.appendChild(li);
}
result.forEach(e => {
const li = document.createElement('li');
li.innerHTML = `${e.title}`;
results.appendChild(li);
});
};
});
change
let result = data.filter(name => match.test(name.title));
to
let result = data.filter(name => match.test(name.title) || match.test(name.tags) || match.test(name.auther));
It may be an idea to filter on all entries of the objects within the Array retrieved from the json.
Here's a minimal reproducable example, using Event Delegation.
See also
document.addEventListener(`click`, handle);
const data = getJSONFakeData();
function handle(evt) {
if (evt.target.id === `search`) {
return searchJSON();
}
}
function searchJSON() {
const resultsDiv = document.querySelector(`#results`);
resultsDiv.textContent = ``;
const nothingFound = isEmpty =>
resultsDiv.insertAdjacentHTML(
`beforeend`,
`<h3>${isEmpty
? `😢 No input`
: `No results found 😢`}</h3>` );
const term = document.querySelector(`#term`).value.trim();
if (term.length < 1) {
return nothingFound(true);
}
const re = new RegExp(term, `gi`);
// filter here
const results = data
.filter( entry => Object.entries(entry)
.find( ([, value]) => re.test(value) )
);
if (results.length) {
let elems = [];
results.forEach( result => {
const res = Object.entries(result)
.reduce( (acc, [key, value]) =>
acc.concat(`<i>${key}</i>: ${value};<br>`), ``);
elems.push(`<li>${res}</li>`);
});
return resultsDiv.insertAdjacentHTML(
`beforeend`,
`<ul>${elems.join(``)}</ul>`);
}
return nothingFound();
}
function getJSONFakeData() {
return [{
title: `title1`,
author: `author1`,
tags: `science, medicine`,
editor: `Springer Verlag`
},
{
title: `title2`,
author: `author2`,
tags: `automotive, engine`,
editor: `Elsevier`
},
{
title: `title3`,
author: `author3`,
tags: `programming, functional, loops`,
editor: `Elsevier`
},
];
}
body {
font: normal 12px/15px verdana, arial;
margin: 2em;
}
<input type="text" id="term" value="Elsevier">
<button id="search">Find</button>
<div id="results"></div>

Having trouble stopping duplicate from being added to array

I have added an voice command prompt to my small ecommerce application. When I command the ai to add a product to the list it adds it and when I try to add it again it doesn't. However, when I try to add a different item the command prompt denies it. I want it to work for all item. I have tried to look up different options including indexOf but that method isn't working either.
function voiceCommand(data) {
const products = new Products();
let alanBtnInstance = alanBtn({
top: '15px',
left: '15px',
onCommand: function (commandData) {
if (commandData.command === "opencart") {
products.openCart();
}
else if (commandData.command === "closecart") {
products.closeCart();
}
else if (commandData.command === "addItem") {
// get cart Items to compare to commandData.name
const cartItem = data
cartItem.forEach(item => {
return item.amount = 1
});
const item = cartItem.map(item => item)
.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
cart = [...cart, item]
function hasDuplicates(arr) {
return new Set(arr).size !== arr.length;
}
if (hasDuplicates(cart)) {
alanBtnInstance.playText(`${item.title} is already in cart`);
return
}
else {
const buttons = [...document.querySelectorAll('.cart-btn')]
buttonsDOM = buttons;
buttons.forEach(button => {
let id = button.dataset.id;
let inCart = cart.find(item => item.id === Number(id));
if (inCart) {
button.innerText = "In Cart";
button.disabled = true;
}
});
products.addCartItem(item);
products.setCartValues(cart);
products.openCart()
return
}
}
},
rootEl: document.getElementById("alan-btn"),
});
}
I simplified the code a little to show the basic principle (I hope I was not mistaken in the logic)
let cart = [];
const addItem = (data, commandData) => {
const item = data
.find(item => item.title.toLowerCase() ===
commandData.name.toLowerCase());
const dup = cart
.find(item => item.title.toLowerCase() ===
commandData.name.toLowerCase());
if (dup) {
//console.log(`${dup.title} is already in cart`)
dup.amount += 1; // just change amount
item.amount -= 1; // calc rest in store
} else {
cart = [...cart, {...item, amount: 1}] // insert new
item.amount -= 1; // calc rest in store
}
}
// store with amounts
const data = [
{id:'0', amount: '100', title: 'a'},
{id:'0', amount: '100', title: 'b'}
]
console.log('Cart before:')
console.log(JSON.stringify(cart))
console.log('Store before:')
console.log(JSON.stringify(data))
// test actions:
addItem(data, {name: 'b'})
addItem(data, {name: 'b'})
addItem(data, {name: 'a'})
console.log('Cart after:')
console.log(JSON.stringify(cart))
console.log('Store after:')
console.log(JSON.stringify(data))
I think your code should be like this:
else if (commandData.command === "addItem") {
// get cart Items to compare to commandData.name
const cartItem = data;
cartItem.forEach(item => {
return item.amount = 1
});
const item = cartItem.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
const dup = cart.find(item => item.title.toLowerCase() === commandData.name.toLowerCase());
if (dup) {
alanBtnInstance.playText(`${item.title} is already in cart`);
return
}
else {
cart = [...cart, item]
const buttons = [...document.querySelectorAll('.cart-btn')]
buttonsDOM = buttons;
buttons.forEach(button => {
let id = button.dataset.id;
let inCart = cart.find(item => item.id === Number(id));
if (inCart) {
button.innerText = "In Cart";
button.disabled = true;
}
});
products.addCartItem(item);
products.setCartValues(cart);
products.openCart()
return
}
}

Categories

Resources