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)
Related
I have a function where I have to return for each "subcontractor" its response for each selection criteria.
Subcontractor object contains a selectionCriteria object. selectionCriteria object contains an array of data for each selectionCriteria a user has responded to.
Each array item is an object, that contains files, id, request (object that contains info about selection criteria user is responding to), response (contains value of the response).
Here is an example of how a subcontractor looks:
This is the function I come up with, but it's quite complex:
const { subcontractors } = useLoaderData<typeof loader>();
const { t } = useTranslation();
const submittedSubcontractors = subcontractors.filter(
(s) => s.status === 'submitted'
);
const subcontractorsResponsesToSelectionCriteria: Array<ISubcontractor> = [];
let providedAnswersResponded: boolean | null = null;
let providedAnswersFiles: Array<IFile> | [] = [];
let providedAnswersRequiresFiles: boolean | null = null;
submittedSubcontractors.forEach((u) => {
u.selectionCriteria.forEach((c) => {
if (c.request.id === criteriaId) {
if (c.response && 'answer' in c.response) {
if (typeof c.response.answer === 'boolean') {
providedAnswersResponded = c.response.answer;
} else {
providedAnswersResponded = null;
}
} else {
providedAnswersResponded = null;
}
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u as ISubcontractor);
}
});
});
How could I simplify this code by using .reduce() method, or maybe even better ideas?
You should start working on reducing the level of nesting in your if/else like so:
function getProvidedAnswersResponded(response: any) {
if (response && ('answer' in response) && (typeof response.answer === 'boolean')) {
return response.answer;
}
return null;
}
submittedSubcontractors.forEach(u => {
u.selectionCriteria.forEach(c => {
if (c.request.id !== criteriaId) {
return;
}
providedAnswersResponded = getProvidedAnswersResponded(c.response);
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u);
});
});
The strategy followed was basically to invert the special cases (such as c.requet.id === criteriaId) and exit the function immediately.
Also, extracting the "provided answer responded" function seems atomic enough to move it to a separate block, giving it more verbosity about what that specific code block is doing.
I just came from the interview, I have implemented multiple filter feature in my assignment.
assignment is live here: https://tooth-store.netlify.app/
here is my code where I am filtering with according to the value of filters state.
const filterData = () => {
let data = [...products];
if (byCategory !== 'all') {
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all') {
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != '') {
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};
Interviewer told me if we will be having a lot of products then going with this approach is not a good idea, so we have to filter at a one go not for all single value of filters.
example: category filter is applied earlier, and now we are changing the rating then it will again filters the category first. so filter it in a one go.
Can anyone explain me in a detail how I have to deal with this, I got blank at that time, but now i am guessing i simply have to check for all filters values with && operator in a single filter
Is there is any other best way?
You can make a common function, where you can check the category against data, here I give you an example in the below code, I make a common function where I get a two param, one is item this is list of items and the second is category user which category against wants data, In the function, I define the some categories array, you can store the categories dynamically in the state and then check if category exists in the categories array, If exists then you can filter the data against the categories list and If you want to see the full example CLICK HERE.
const func = (item, category) => {
let newArr = [];
if (category !== "all") {
const categories = ["electronics", "men's clothing", "jewelery"];
const ratings = [1.9, 2.5, 6.7];
if (categories.includes(category)) {
newArr = item.category.toLowerCase() === category.toLowerCase();
} else if (ratings.includes(category)) {
newArr = Math.floor(item.rating.rate) === category;
} else {
newArr = item.title.toLowerCase().includes(category.toLowerCase());
}
}
return newArr;
};
const filterData = () => {
let data = [...products];
data = data.filter((item) => {
return func(item, byCategory);
});
return data;
};
you can use this method where your filter option will run for only one type at a time just you have to pass a parameter like this
const filterData = (type) => {
let data = [...products];
if (byCategory !== 'all' && type == 'cat') { // call this filterData('cat')
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all'&& type == 'rate') { // call this filterData('rate')
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != ''&& type == 'search') { // call function by filterData('search')
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};
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] : ""));
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");
}
can anyone tell me where i'm going wrong?
I'm desperately trying to get my jquery carousel to re-render with the results of a filtered array - but really hitting issues today.
I grab the carouselId from its div with wrapper
i get the api array of objects & store it in moviesArray.
Then the key thing is that when someone has typed in the input, it re-renders the carousel based on that newly filtered array.
function populateCarousel() {
var moviesArray = [];
var tempResultsArr = [];
fetch('http://localhost:3000/getdata')
.then((res) => res.json())
.then((json) => {
moviesArray = json;
// ===== if NO searchTerm is entered ====
// THIS ONE WORKS
if (typeof searchTerm === 'undefined') {
moviesArray.map((each) => {
var eachTile = document.createElement("a");
var imageForEachTile = document.createElement("img");
imageForEachTile.setAttribute('src', each.poster);
eachTile.setAttribute("class", "carousel-item thumbnail-item");
eachTile.setAttribute("href", "#linkToSomeWhere");
eachTile.innerHTML = each.title;
eachTile.appendChild(imageForEachTile);
wrapper.appendChild(eachTile);
})
} else {
// ===== if a searchTerm *IS* entered ====
// THIS ONE DOESN'T WORK!
while (wrapper.firstChild) {
wrapper.removeChild(wrapper.firstChild);
}
function checkWords(searchTerm, arr) {
let st = searchTerm.toLowerCase();
return arr.filter(each => each.title.toLowerCase().indexOf(st) === 0);
}
let newArr = checkWords(searchTerm, moviesArray);
newArr.map((each) => {
var eachTile = document.createElement("a");
var imageForEachTile = document.createElement("img");
imageForEachTile.setAttribute('src', each.poster);
eachTile.setAttribute("class", "carousel-item thumbnail-item");
eachTile.setAttribute("href", "#linkToSomeWhere");
eachTile.innerHTML = each.title;
eachTile.appendChild(imageForEachTile);
wrapper.appendChild(eachTile);
})
// this logs out correctly & when i go into the html
// the correct searchresults are there, a and img tags.
// but they dont show.
console.log('wrapper: ', wrapper);
}
})
}
you notice the .map((each)... is identical on both sides of the IF/ELSE.
It does create the elements and attributes correctly, and does append them because a) i can log them out but b) i can actually inspect the HTMl in chrome and see them there.
But why aren't they actually showing?