no-nested-ternary on a nested and complicated array of objects - javascript

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;
}
}),
),

Related

Javascript - filter if statement doesnt work

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));

List sorting method not updating order

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;
},
},

how to push into nested array in reducer

Above image shows what happens when I trying to dispatch a reply action.
Currently trying to push the input field value etc into the replyComments...but it is still empty even when dispatching this action.... the reducer is checking the majorIndex first and then the minorIndex, from there it should push it into the replyComments. Any one knows how I can fix this?
Link to working Codesandbox
case types.REPLY_COMMENT: {
return {
...state,
enableToggleComment: true,
imageData: state.imageData.map(image => image.id === action.majorIndex ?
{
...image,
comments: {
[action.minorIndex]: {
replyComments: [...image.comments.replyComments, { comment: action.newComment, likeComment: image.toggle, enableReply: false }]
}
}
} : image
)
}
}
EDIT:
I know noticed it's due to a missing key to [action.minorIndex] and when using the ? operator and want to return an object you should add the parenthesis, otherwise you are declaring a scope (like i did whoops)
case types.REPLY_COMMENT: {
return {
...state,
enableToggleComment: true,
imageData: state.imageData.map(image => image.id === action.majorIndex ?
({
...image,
comments: {
[action.minorIndex]: {
replyComments: [...image.comments[action.minorIndex].replyComments, { comment: action.newComment, likeComment: image.toggle, enableReply: false }],
}
}
}) : image
)
}
}
I've only added action.replyText but you should be able to add the rest now
case types.REPLY_COMMENT: {
return {
...state,
enableToggleComment: true,
imageData: state.imageData.map(image =>
image.id === action.generalIndex
? {
...image,
comments: image.comments.map((comment, i) =>
i === action.majorIndex
? {
...comment,
replyComments: [
...comment.replyComments,
action.replyText
]
}
: comment
)
}
: image
)
};
}

Filter with map but keep the indice

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;
})

lodash orderby with null and real values not ordering correctly

I have an Angular 2 typescript application that is using lodash for various things.
I have an array of objects that I am ordering using a property in the object...
_.orderBy(this.myArray, ['propertyName'], ['desc']);
This works well however my problem is that sometimes 'propertyName' can have a null value.
These are ordered as the first item in a descending list, the highest real values then follow.
I want to make these null values appear last in the descending ordering.
I understand why the nulls come first.
Does anyone know how to approach this?
The _.orderBy() function's iteratees can use a method instead of a string. Check the value, and if it's null return an empty string.
const myArray = [{ propertyName: 'cats' }, { propertyName: null }, { propertyName: 'dogs' }, { propertyName: 'rats' }, { propertyName: null }];
const result = _.orderBy(myArray, ({ propertyName }) => propertyName || '', ['desc']);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
The check can be simple (like the one I've used), which converts all falsy values to an empty string:
propertyName || ''
If you need a stricter check, you can use the ternary operator, and handle just null values:
propertyName === null ? '' : propertyName
Edit: Example with multiple ordering:
const result = _.orderBy(myArray, (item) => [get(item, 'propertyName', 0), get(item, 'propertyName2')], ['desc', 'asc']);
This will order by propertyName then propertyName2.
If propertyName is undefined/null then its default order will be set to 0. (and therefore will be displayed at last because of desc ordering on the propertyName field). In such case, propertyName2 will therefore determine the ordering.
The code I needed looks like this...
_.orderBy(this.myArray, [( o ) => { return o.myProperty || ''}], ['desc']);
Just for future reference to others you can do this to sort ascending with falsey values at the end.
items =>
orderBy(
items,
[
i => !!i.attributeToCheck,
i => {
return i.attributeToCheck ? i.attributeToCheck.toLowerCase() : ''
}
],
['desc', 'asc']
)
mine looks like this. PropName and sort are both variables in my solution
return _.orderBy( myarray, [
( data ) => {
if ( data[propName] === null ) {
data[propName] = "";
}
return data[propName].toLowerCase();
}
], [sort] );
I wanted tolowercase because otherwise the sorting is not correct if different casings
This will put bad values at the bottom, and it differentiates between numbers and strings.
const items = [] // some list
const goodValues = isAscending => ({ value }) => {
if (typeof value !== 'string' && isNaN(value)) {
return isAscending ? Infinity : -Infinity
}
return value || ''
}
const sortedItems = orderBy(
items,
[goodValues(isAscending), 'value'],
[isAscending ? 'asc' : 'desc']
)
This worked for me
orders = [{id : "1", name : "test"}, {id : "1"}];
sortBy = ["id", "name"];
orderby(
orders,
sortBy.map(s => {
return (r: any) => {
return r[s] ? r[s] : "";
};
})),
);
I created a function for this (ts code):
const orderByFix = (array: any[], orderKeys: string[], orderDirs: ('asc' | 'desc')[]) => {
const ordered = orderBy(array, orderKeys, orderDirs);
const withProp = ordered.filter((o) => orderKeys.every(k => o[k]));
const withoutProp = ordered.filter((o) => !orderKeys.every(k => o[k]));
return [...withProp, ...withoutProp];
};
I've extended gwendall's answer to also handle case when "order keys" are functions (_.orderBy allows that)
const orderByFix = (
array: any[],
orderKeys: (string | ((o: any) => any))[],
orderDirs: ('asc' | 'desc')[]
) => {
const ordered = orderBy(array, orderKeys, orderDirs)
const withProp = ordered.filter((o) =>
orderKeys.every((k) => {
if (typeof k === 'string') {
return o[k]
} else if (typeof k === 'function') {
return k(o)
} else {
throw Error(`Order key must be string or function not ${typeof k}`)
}
})
)
const withoutProp = ordered.filter(
(o) =>
!orderKeys.every((k) => {
if (typeof k === 'string') {
return o[k]
} else if (typeof k === 'function') {
return k(o)
} else {
throw Error(`Order key must be string or function not ${typeof k}`)
}
})
)
return [...withProp, ...withoutProp]
}

Categories

Resources