I'm trying to get some insight into why this sort function isn't working.
In theory, it should work the same as this: https://codepen.io/levit/pen/abmXgBR
I have a list I'm grabbing from an API:
<BookCard v-for='book in filteredBooks' :key='book.id' :book='book' />
I've got a filter for search working, but my sort isn't. Here is my data with computed properties/methods:
data() {
return {
books: [],
order: 1, // Ascending
search: '',
};
},
computed: {
filteredBooks() {
return this.filterBySearch((this.sortByRating(this.books)));
},
},
methods: {
filterBySearch(books) {
return books.filter((book) => book.volumeInfo.title
.toLowerCase().match(this.search.toLowerCase()));
},
sortByRating(books) {
return books
.sort((r1, r2) => (r2.volumeInfo.averageRating - r1.volumeInfo.averageRating)
* this.order);
},
sort() {
this.order *= -1;
},
},
Finally, I have a button to switch the order:
<button v-bind:class="order === 1 ? 'descending' : 'ascending'" #click="sort">
Reader Rating
</button>
Any insight into what I might be getting wrong would be very helpful as I'm new to Vue.
Thank you.
Try to not pass the data property as an argument since it's available inside the methods and just sort the filtered books not the original property because the sort affects it :
computed: {
filteredBooks() {
let filtered= this.filterBySearch().slice();
return this.sortByRating(filtered)
},
},
methods: {
filterBySearch() {
return this.books.filter((book) => book.volumeInfo.title
.toLowerCase().match(this.search.toLowerCase()));
},
sortByRating(books) {
return books.sort((r1, r2) => {
if (
typeof r2.volumeInfo.averageRating === "number" &&
typeof r1.volumeInfo.averageRating === "number"
) {
return (
(r2.volumeInfo.averageRating - r1.volumeInfo.averageRating) *
this.order
);
} else {
return this.order * -1;
}
});
},
sort() {
this.order *= -1;
},
},
Related
Hey guys i got this code where i wanted filter array and return only if doesnt equal to type RJ_TIME and seatClassKey TRAIN_1ST_CLASS at same time.
This doesnt work and behave like OR, so it will return me object that isnt RJ_TIME or TRAIN_1ST_CLASS.
.filter((pClass) => {
if (isFirstClassSoldOut) {
if (pClass.type !== "RJ_TIME" && pClass.seatClassKey !== "TRAIN_1ST_CLASS") {
return pClass
}
} else {
return pClass
}
})
.map((pClass) => (
The condition in the callback for filter needs return a boolean value which is what filter uses to determine whether the iterated element is returned or not.
Here only objects 2 & 4 will be returned as they're the only objects whose properties match the condition.
const isFirstClassSoldOut = true;
const arr = [
{ type: 'RJ_TIME', seatClassKey: 'TRAIN_1ST_CLASS' },
{ type: 'RJ_TIME2', seatClassKey: 'TRAIN_1ST_CLASS2' },
{ type: 'RJ_TIME3', seatClassKey: 'TRAIN_1ST_CLASS' },
{ type: 'RJ_TIME4', seatClassKey: 'TRAIN_1ST_CLASS4' },
];
arr.filter(pClass => {
return isFirstClassSoldOut
&& (pClass.type !== 'RJ_TIME'
&& pClass.seatClassKey !== 'TRAIN_1ST_CLASS');
})
.map(pClass => console.log(pClass));
I have a complicated array of objects and I want to change a property that deeply nested in the array.
I did the following function and it is work, but the problem is that the eslint throw me an error because there is a nested ternery to check if i am on the currect place in an array:
toggleSwitchDetector(state: Array<IOpticalHeadUnit>, { payload }: any) {
return [
...state.map((opticalHeadUnit) =>
opticalHeadUnit.name === payload.opticalHeadUnitName
? {
...opticalHeadUnit,
detectors: [
...opticalHeadUnit.detectors.map(
(detector, index) => ({
...detector,
status: {
...detector.status,
value: //Nested ternery. Bad practice.
index === payload.index
? detector.status
.value === 0
? 1
: 0
: detector.status.value,
},
}),
),
],
}
: opticalHeadUnit,
),
];
}
is there a simpler way to approach the modification of the deeply nested property?
EDIT:
the code after I modified it with the answers:
const getDetectorStatusValue=(index:number,payload:number,detector:IDetector)=>{
if(index === payload)
{
if(detector.status.value===0)
return 1
return 0
}
return detector.status.value
}
toggleSwitchDetector(state: Array<IOpticalHeadUnit>, { payload }: any) {
return state.map((opticalHeadUnit) =>
opticalHeadUnit.name === payload.opticalHeadUnitName
? {
...opticalHeadUnit,
detectors: [
...opticalHeadUnit.detectors.map(
(detector,index) => ({
...detector,
status: {
...detector.status,
value:getDetectorStatusValue(index,payload.index,detector)
},
}),
),
],
}
: opticalHeadUnit,
);
Maybe something like this
toggleSwitchDetector(state: Array<IOpticalHeadUnit>, { payload }: any) {
return state.map((opticalHeadUnit) => getState(payload, opticalHeadUnit)
}
getStatus(payload, detector) {
return {
...detector.status,
value: //Nested ternery. Bad practice.
index === payload.index
? detector.status
.value === 0
? 1
: 0
: detector.status.value,
};
}
getDetectors(payload, opticalHeadUnit) {
return opticalHeadUnit.detectors.map(
(detector, index) => ({
...detector,
status: getStatus(payload, detector),
}),
);
}
getState(payload, opticalHeadUnit) {
opticalHeadUnit.name === payload.opticalHeadUnitName
? {
...opticalHeadUnit,
detectors: getDetectors(payload, opticalHeadUnit);
}
: opticalHeadUnit,
}
you don't need [...array], replace it with array
If you want to get rid of the nested ternary operator, you could write slightly more code but it will be more readable.
You can check if index === payload.index with a simple if statement and only in that case go deep in your object to eventually check detector.status.value === 0 ? 1 : 0
Otherwise just return detector as is;
...opticalHeadUnit.detectors.map(
(detector, index) => ({
if (index === payload.index) {
return {
...detector,
status: {
...detector.status,
value: detector.status.value === 0 ? 1 : 0
},
}
} else {
return detector;
}
}),
),
I am trying to create three search fields. with an input and two selectors. somehow I managed to work with two but with three it is not working I need help
computed: {
filterMembers: function () {
let filtered = this.trabalinhos;
if (this.searchText) {
filtered = this.trabalinhos.filter(
(m) => m.title.toLowerCase().indexOf(this.searchText) > -1
);
}
if (this.searchTrabalho) {
filtered = filtered.filter(
(m) =>
m.title.toLowerCase().indexOf(this.searchTrabalho) ==
this.searchTrabalho.toLowerCase() > -1
);
}
if (this.select) {
filtered = filtered.filter(
(m) =>
m.title.toLowerCase().indexOf(this.select) ==
this.select.toLowerCase() > -1
);
}
return filtered;
},
},
Consider using includes.
computed: {
filterMembers() {
return this.trabalinhos
.filter(member => member.title.toLowerCase().includes(this.searchText))
.filter(member => member.title.toLowerCase().includes(this.searchTrabalho))
.filter(member => member.title.toLowerCase().includes(this.select))
}
}
OR
computed: {
filterMembers() {
return this.trabalinhos
.filter(member =>
member.title.toLowerCase().includes(this.searchText) &&
member.title.toLowerCase().includes(this.searchTrabalho) &&
member.title.toLowerCase().includes(this.select)
)
}
}
I'm working on a computed function for comparing two arrays... one keywords in data and another returned by computed keywords_list. When computed compare, he doesn't take in count values getting before by Axios in the first array ...
I don't know what to do ...
my code:
{
beforeCreate() {
this.$axios({
method: 'get',
url: '/api/rest/alerts/'
}).then(response => {
if ((response.data.keywords.length == 0) && (response.data.countries.length == 0)) {
this.alertEmpty = 'Aucune alerte de créée'
} else {
this.alert = response.data
this.keywords = response.data.keywords
this.countriesKeywords = response.data.countries
this.allSectorsSelected = response.data.all_sectors
}
})
},
data() {
return {
categories: "",
alert: '',
alertEmpty: '',
countriesKeywords: [],
countrySelected: '',
keywords: [],
update: false,
sectorSelected: "",
alerts: [],
keywordSelected: "",
errorKeyword: '',
successModification: '',
allSectorsSelected: null,
};
},
computed: {
keywords_list: function() {
for (var i = 0; i < this.sectors.length; i++) {
if (this.sectors[i].id == this.sectorSelected) {
return this.sectors[i].keywords;
}
}
},
allKeywordsInSector: function() {
if (this.keywords_list.every(item => this.keywords.indexOf(item) >= 0)) {
return true;
}
return false;
},
}
}
thanks for your help
very little information and it is unclear how the api result affects your computed values
The only thing i can say, properties in Vue doesn't reactive. So if sections isn't static, computed property sectorSelected will change only if sectorSelected changes
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
I wanna to filter and then map the array of object, but when do this the filter make a new array and give me different indices, is possible to do this using filter or should i use another way.
this.props.todos = [
{
task:'Daily meet',
status:'incomplete'
},
{
task:'Play videogame'
status:'complete'
}
]
this.props.todos.filter(todos => todos.status === 'complete').map((todos, i) => {
return (<p>{todos.status} - Everythin Ok</p>)
//here i got i = 0
}));
this.props.todos.filter(todos => todos.status === 'incomplete').map((todos, i) => {
return (<p>{todos.status} - You have some task incomplete</p>)
//and here i got i = 0 too i wanna this to be like the original array
}));
Just use map, no need to filter. You can return either component like this:
this.props.todos = [
{
task: 'Daily meet',
status: 'incomplete'
},
{
task: 'Play videogame'
status: 'complete'
}
]
this.props.todos.map(todo => {
if (todo.status === 'complete') {
return (<p>{todo.status} - Everythin Ok</p>);
}
if (todo.status === 'incomplete') {
return (<p>{todo.status} - You have some task incomplete</p>);
}
return null;
})