I'm trying to change the title and description properties on an individual task without mutating the state. The nesting is complex
dayColumns: [
{
id: 'monday',
tasks: [{
key: cellTime,
dayOfWeek: columnDay,
date: date,
timeStart: cellTime,
timeEnd: cellTime + blockInterval * 60000,
initalBlockSize: blockInterval,
title: '',
description: '',
},
{
key: cellTime,
dayOfWeek: columnDay,
date: date,
timeStart: cellTime,
timeEnd: cellTime + blockInterval * 60000,
initalBlockSize: blockInterval,
title: '',
description: '',
}],
},
{
id: 'tuesday',
tasks: [],
},
{
id: 'wednesday',
tasks: [],
},
{
id: 'thursday',
tasks: [],
},
{
id: 'friday',
tasks: [],
},
{
id: 'saturday',
tasks: [],
},
{
id: 'sunday',
tasks: [],
},
];
This is the function I have so far. I have adjusted this many times and I think my errors are with the returns.
case SET_TASK_TEXT:
const { textType, newText, key, dayOfWeek } = action.payload;
const newTextDayColumns = state.dayColumns.map((column) => {
if (column.id === dayOfWeek) {
const newTasks = column.tasks.map(
(task) => {
if (task.key === key && textType === 'title') {
return { ...task, title: newText };
}
if (task.key === key && textType === 'description') {
return { ...task, description: newText };
}
}
/** Change title or description according to #param textType */
);
return [...column.tasks, newTasks];
}
return {...state.dayColumns, newTextDayColumns}
});
return {
...state,
dayColumns: newTextDayColumns,
};
your last {...state.dayColumns, newTextDayColumns} is wrong, you should only return the same column or a new one with updated tasks. Also, you should not return [...column.tasks, newTasks] from your map.
const newTextDayColumns = state.dayColumns.map((column) => {
// return column if no match
if (column.id !== dayOfWeek) return column;
const newTasks = column.tasks.map(
(task) => {
if (task.key === key && textType === 'title') {
return { ...task, title: newText };
}
if (task.key === key && textType === 'description') {
return { ...task, description: newText };
}
// it's good to return task if conditionals above fail
return task;
}
);
// return new column with updated task
return {...column, tasks: newTasks};
});
Using the spread operator almost always mutates your current state since while using it with an array, you are taking all its individual elements and creating a new array to place then into.
Same goes for objects.
Although this can be done in a slightly better way it would still result in such enormous, confusing and hard to maintain code.
Immutability Helper package solves this exact problem by simplifying the way you update states.
Related
My brain froze with this advanced filtering. This task has exceeded my basic knowledge of filter, map etc.
Here I have an array with nested objects with array:
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
You may have seen this sort of style if you've worked with React Native (RN). This question is not for RN. I need to perform a filter on the name property in the nested array and when I get a match, I must return the format as the DATA variable.
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
console.log(results);
};
My limited knowledge of deep filtering returns the basic filtering for the data array but need to retain the structure for DATA. The expected results I'd expect:
// I'm now querying for "ZAMASU"
const handleFiltering = (value='ZAMA') => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
// console.log(results) should now be
// [
// {
// title: 'Dragon Balls Z',
// data: [
// { id: 2, name: 'Zamasu' }
// ]
// }
// ];
};
What comes to mind is the use of {...DATA, something-here } but my brain has frozen as I need to get back the title property. How to achieve this, please?
Another solution would be first use filter to find only objects containing the name in data passed through the argument, subsequently mapping data.
Here is your adjusted filter method
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.filter((obj) =>
obj.data.some((character) => character.name.toLowerCase() === _value)
).map((obj) => ({
title: obj.title,
data: obj.data.filter(
(character) => character.name.toLowerCase() === _value
),
}));
console.log(results);
};
You can use reduce method of array. First find out the object inside data array and then add that to accumulator array as new entry by preserving the original structure.
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs', where: 'tv' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
let handleFiltering = (value='tv') => {
return DATA.reduce((acc,d) => {
let obj = d.data.find(a => a.name?.toLowerCase().includes(value.toLowerCase())
|| a.where?.toLowerCase().includes(value.toLowerCase()));
obj ? acc.push({...d, data:[obj]}) : null;
return acc;
}, []);
}
let result = handleFiltering();
console.log(result);
Given an object or array, I want to be able to determine if the path exists or not.
Given - Example 1
const spath = "data/message";
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: 'true'
},
time: 'UTC',
class: 'Finals'
}
it should return true because message can be found in body.data.message else return false.
Given - Example 2
const spath = "data/message/details/lastGreeting";
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
}
it should return true because lastGreeting can be found in body.data.message.details.lastGreeting else return false.
The other condition is when the body consists of an array
Given - Example 3
const spath = "data/area/NY";
const body = {
data: {
school: 'yaba',
age: 'tolu',
names : ['darious'],
area: [{
NY: true,
BG: true
]]
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
}
it should return true because NY can be found in body.data.area[0].NY else return false.
This is the solution I came up with
const findPathInObject = (data, path, n) => {
console.log('entered')
console.log(data, path)
if(!data){
return false
}
let spath = path.split('/');
for(let i = 0; i<n; i++){
let lastIndex = spath.length - 1;
if(spath[i] in data && spath[i] === spath[lastIndex]){
return true
}
const currentIndex = spath[i];
// spath.splice(currentIndex, 1);
return findPathInObject(data[spath[currentIndex]], spath[i+1], spath.length)
}
return false
}
console.log(findPathInObject(body, spath, 3))
You could take some checks in advance and check if path is an empry string, then exit with true.
By having an array, you could exit early by checking the elements of the array with the actual path by omitting the indices.
For the final check of a key, you could check the existence of it and return the result of the recursove call with the rest path or return false, if the key is not in the object.
const
findPathInObject = (data, path) => {
if (!path) return true;
if (!data || typeof data !== 'object') return false;
if (Array.isArray(data)) return data.some(d => findPathInObject(d, path));
const
spath = path.split('/'),
key = spath.shift();
return key in data
? findPathInObject(data[key], spath.join('/'))
: false;
};
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' }, "data/message", 3)); // true
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/message/details/lastGreeting", 3)); // true
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/area/NY", 3)); // true
find
For this answer, I'm going to provide a tree with varying degrees of nesting of objects and arrays -
const tree =
{ data:
{ school: "yaba", age: "tolu", message: "foo" }
, classes:
[ { name: "math" }, { name: "science" } ]
, deep:
[ { example:
[ { nested: "hello" }
, { nested: "world" }
]
}
]
}
Generators are a fantastic fit for this type of problem. Starting with a generic find which yields all possible results for a particular path -
function find (data, path)
{ function* loop (t, [k, ...more])
{ if (t == null) return
if (k == null) yield t
else switch (t?.constructor)
{ case Object:
yield *loop(t[k], more)
break
case Array:
for (const v of t)
yield *loop(v, [k, ...more])
break
}
}
return loop(data, path.split("/"))
}
Array.from(find(tree, "classes/name"))
Array.from(find(tree, "deep/example/nested"))
Array.from(find(tree, "x/y/z"))
[ "math", "science" ]
[ "hello", "world" ]
[]
find1
If you want a function that returns one (the first) result, we can easily write find1. This is particularly efficient because generators are pauseable/cancellable. After the first result is found, the generator will stop searching for additional results -
function find1 (data, path)
{ for (const result of find(data, path))
return result
}
find1(tree, "data/school")
find1(tree, "classes")
find1(tree, "classes/name")
find1(tree, "deep/example/nested")
find1(tree, "x/y/z")
"yaba"
[ { name: "math" }, { name: "science" } ]
"math"
"hello"
undefined
exists
If you care to check whether a particular path exists, we can write exists as a simple specialisation of find1 -
const exists = (data, path) =>
find1(data, path) !== undefined
exists(tree, "data/school")
exists(tree, "classes")
exists(tree, "deep/example/nested")
exists(tree, "x/y/z")
true
true
true
false
demo
Expand the snippet below to verify the results in your own browser -
function find (data, path)
{ function* loop (t, [k, ...more])
{ if (t == null) return
if (k == null) yield t
else switch (t?.constructor)
{ case Object:
yield *loop(t[k], more)
break
case Array:
for (const v of t)
yield *loop(v, [k, ...more])
break
}
}
return loop(data, path.split("/"))
}
function find1 (data, path)
{ for (const result of find(data, path))
return result
}
const tree =
{ data:
{ school: "yaba", age: "tolu", message: "foo" }
, classes:
[ { name: "math" }, { name: "science" } ]
, deep:
[ { example:
[ { nested: "hello" }
, { nested: "world" }
]
}
]
}
console.log("find1")
console.log(find1(tree, "data/school"))
console.log(find1(tree, "classes"))
console.log(find1(tree, "classes/name"))
console.log(find1(tree, "deep/example/nested"))
console.log(find1(tree, "x/y/z"))
console.log("find")
console.log(Array.from(find(tree, "classes/name")))
console.log(Array.from(find(tree, "deep/example/nested")))
console.log(Array.from(find(tree, "x/y/z")))
Strictly spoken, body.data.area[0].NY is not in the path of body, sorry. body.data.area is in the path. For the object without body.data.area as array here's a solution. If you want to include objects within arrays as part of an objects path, the solution will be more complex
const spath = "data/area/NY";
const spath2 = "data/message/details/lastGreeting";
const notPath = "data/message/details/firstGreeting";
const body = {
data: {
school: 'yaba',
age: 'tolu',
names : ['darious'],
area: {
NY: true,
BG: true
},
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
};
console.log(`${spath} exists? ${ exists(body, spath) && `yep` || `nope`}`);
console.log(`${spath2} exists? ${ exists(body, spath2) && `yep` || `nope`}`);
console.log(`${notPath} exists? ${ exists(body, notPath) && `yep` || `nope`}`);
function exists(obj, path) {
const pathIterable = path.split("/");
while (pathIterable.length) {
const current = pathIterable.shift();
// no path left and exists: true
if (pathIterable.length < 1 && current in obj) { return true; }
// up to now exists, path continues: recurse
if (current in obj) { return exists(obj[current], pathIterable.join("/")); }
}
// no solution found: false
return false;
}
You can check this solution. Will also check for array of objects.
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: {
content: 'now',
details: {
lastGreeting: true,
},
},
area: [
{
NY: true,
BG: true,
},
],
},
time: 'UTC',
class: 'Finals',
};
const spath1 = 'data/message';
const spath2 = 'data/message/details/lastGreeting';
const spath3 = 'data/area/NY';
const spath4 = 'data/area/NY/Test';
console.log(`${spath1}: `, isPathExists(body, spath1.split('/'), 0));
console.log(`${spath2}: `, isPathExists(body, spath2.split('/'), 0));
console.log(`${spath3}: `, isPathExists(body, spath3.split('/'), 0));
console.log(`${spath4}: `, isPathExists(body, spath4.split('/'), 0));
function isPathExists(data, pathArr, i) {
const key = pathArr[i];
if (Array.isArray(data)) {
for (let value of data) {
if (isObject(value)) return isPathExists(value, pathArr, i);
}
} else if (data.hasOwnProperty(key)) {
if (key === pathArr[pathArr.length - 1]) return true;
return isPathExists(data[key], pathArr, i + 1);
} else return false;
return true;
}
function isObject(a) {
return !!a && a.constructor === Object;
}
With help of a colleague we eventually came up with something simple and easy to comprehend that really suits our needs. The answer with the yield implementation solves the problem but we were looking for something someone can read and easily understand in the codebase. We wanted to be able to check if the path exists in the object as well as get the value.
So we added a third param called returnValue - by default it will always return the value. If we don't want it to do that, we can set the value of the return value to false and the function will check if the path am looking exists, if it does, it will return true else return false
This is what we finally came up with
const find = (path, data) => {
if (Array.isArray(data)) {
data = data[0];
}
for (const item in data) {
if (item === path) {
return data[item];
}
}
return null;
};
const findPath = (fullPath, fullData, returnValue = true) => {
const pathArray = fullPath.split('/');
let findResult = fullData;
for (const pathItem of pathArray) {
findResult = find(pathItem, findResult);
if (!findResult) {
if (!returnValue) return false;
return null;
}
}
if (!returnValue) return true;
return findResult;
};
const body = {
name: 'mike',
email: 1,
data: {
school: [
{
testing: 123
}
]
}
}
console.log(findPath('data/school/testing', body))
Here is a clean, iterative solution using object-scan
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.1.2/lib/index.min.js';
const data1 = { data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' };
const data2 = { data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };
const data3 = { data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };
const path1 = 'data/message';
const path2 = 'data/message/details/lastGreeting';
const path3 = 'data/area/NY';
const exists = (data, n) => objectScan([n.replace(/\//g, '.')], {
useArraySelector: false,
rtn: 'bool',
abort: true
})(data);
console.log(exists(data1, path1));
// => true
console.log(exists(data2, path2));
// => true
console.log(exists(data3, path3));
// => true
</script>
Disclaimer: I'm the author of object-scan
I have this object below. I was wondering how I can select a specific item and update a property. For example. Item 1 I want to add a task in the array.
item: {
'item-1': {
id: 'item-1',
title: 'To do',
task: ['task-1', 'task-2', 'task-3', 'task-4']
},
'item-2': {
id: 'item-2',
title: 'In progress',
task: []
},
I currently have
const getItem = {...state.items['item-1']}
const newTaskList = [...getItem.task, newTask.id]
const newState = {
...state,
items: {
...state.items,
//How do I spread new array correctly in item 1?
//...state.items['item-1'].task
}
};
You need to use the object key i.e item-1 and clone the properties for it and add the new list for the task key. In short you need to clone at each level of the object before overriding the key that you wish to update
const newState = {
...state,
items: {
...state.items,
'item-1': {
...state.items['item-1'],
task: newTaskList
}
}
};
Assuming the starting point:
let state = {
items: {
'item-1': {
id: 'item-1',
title: 'To do',
task: ['task-1', 'task-2', 'task-3', 'task-4']
},
'item-2': {
id: 'item-2',
title: 'In progress',
task: []
},
}
};
If you want to add a task to item-1's task array without modifying things in place (which is important in React state), you have to copy state, items, item-1, and item-1's task:
let newState = {
...state,
items: {
...state.items,
'item-1': {
...state.items['item-1'],
task: [...state.items['item-1'].task, newTask]
}
}
};
Live Example:
let state = {
items: {
'item-1': {
id: 'item-1',
title: 'To do',
task: ['task-1', 'task-2', 'task-3', 'task-4']
},
'item-2': {
id: 'item-2',
title: 'In progress',
task: []
},
}
};
let newTask = "task-4";
let newState = {
...state,
items: {
...state.items,
'item-1': {
...state.items['item-1'],
task: [...state.items['item-1'].task, newTask]
}
}
};
console.log(newState);
In lodadash you can get and set nested object from an object, here is my own implementation of it:
//helper to get prop from object
const get = (object, path, defaultValue) => {
const recur = (object, path) => {
if (object === undefined) {
return defaultValue;
}
if (path.length === 0) {
return object;
}
return recur(object[path[0]], path.slice(1));
};
return recur(object, path);
};
//helper to set nested prop in object
const set = (
state,
statePath,
modifier
) => {
const recur = (result, path) => {
const key = path[0];
if (path.length === 0) {
return modifier(get(state, statePath));
}
return Array.isArray(result)
? result.map((item, index) =>
index === Number(key)
? recur(item, path.slice(1))
: item
)
: {
...result,
[key]: recur(result[key], path.slice(1)),
};
};
const newState = recur(state, statePath);
return get(state, statePath) === get(newState, statePath)
? state
: newState;
};
let state = {
items: {
'item-1': {
id: 'item-1',
title: 'To do',
task: ['task-1', 'task-2', 'task-3', 'task-4'],
},
'item-2': {
id: 'item-2',
title: 'In progress',
task: [],
},
},
};
console.log(
set(
state,
['items','item-1','task'],
(tasks)=>tasks.concat('new task')
)
);
You can put the get and set in a library and it would make setting deeply nested values easier on the eyes of future readers of your code.
I have two arrays of objects.
const details = [
{
ciphertext: 1234,
buyer: {
op_timezone: 7689,
op_city: 'Name1',
},
assignment_info: {
info: {
end_data: 1456,
start_date: 2389,
}
}
},
{
ciphertext: 5678,
buyer: {
op_timezone: 4568,
op_city: 'Name2',
},
assignment_info: {
info: {
end_data: 3467,
start_date: 8753,
}
}
},
];
const jobIds = [
{
id: 1,
},
{
id: 2,
},
];
I need to combine two arrays and take the assignment_info.info and buyer fields from each object.
function getDetailsBuyersWithJobIds(jobIds, details) {
return jobIds.map((item, index) => ({
...item,
...details[index].buyer,
}));
};
function getDetailsAssignmentInfosWithJobIds(jobIds, details) {
return jobIds.map((item, index) => ({
...item,
...details[index].assignment_info.info,
}));
};
The question is, how can two functions be combined into one?
That there would be no duplicate function, since they perform the same thing.
You can do a generic mapping function and pass it a getter function that will be able to fetch the proper data, not sure it will help the global readibility though.
What do you think about that?
const genericMapper = (getter) => (item, index) => ({
...item,
...getter(details[index]),
});
function getDetailsBuyersWithJobIds(jobIds, details) {
return jobIds.map(genericMapper(it => it.buyer));
};
function getDetailsAssignmentInfosWithJobIds(jobIds, details) {
return jobIds.map(genericMapper(it => it.assignment_info.info));
};
const details = [
{
ciphertext: 1234,
buyer: {
op_timezone: 7689,
op_city: 'Name1',
},
assignment_info: {
info: {
end_data: 1456,
start_date: 2389,
}
}
},
{
ciphertext: 5678,
buyer: {
op_timezone: 4568,
op_city: 'Name2',
},
assignment_info: {
info: {
end_data: 3467,
start_date: 8753,
}
}
},
];
const jobIds = [
{
id: 1,
},
{
id: 2,
},
];
console.log(getDetailsBuyersWithJobIds(jobIds, details));
console.log(getDetailsAssignmentInfosWithJobIds(jobIds, details));
You can add values on return object based on condition something like this
const details = [{ciphertext: 1234,buyer: {op_timezone: 7689,op_city: 'Name1',},assignment_info: {info: {end_data: 1456,start_date: 2389,}}},{ciphertext: 5678,buyer: {op_timezone: 4568,op_city: 'Name2',},assignment_info: {info: {end_data: 3467,start_date: 8753,}}},];
const jobIds = [{id: 1,},{id: 2,},];
function getDetails(jobIds, details, props = {
getBuyer: true
}) {
return jobIds.map((item, index) => ({
...item,
...(props.getBuyer && { ...details[index].buyer
}),
...(props.getAssignment && { ...details[index].assignment_info.info
})
}));
};
console.log(getDetails([1], details, {
getBuyer: true
}))
console.log(getDetails([1], details, {
getAssignment: true
}))
Here props = { getBuyer: true} used to set a default value.
I am trying to build a workout app using Vue, where you can generate a workout depending on chosen preferences. An user can select some options and then click button to generate a workout. Collected data is an object of arrays and each array contains objects of chosen options (for example: allowed duration of a workout, difficulty, prefered exercises)
this.generatorData = {
types: [],
style: [],
muscleGroups: [],
difficulty: [
{ title: 'Beginner', ..some properties },
{ title: 'Intermediate', .. }
]
}
I also have a set of exercises that have the same properties as a generated object, but are predefined.
exercises: [
{
title: 'Name',
type: 'Weights',
style: ['Strength Building'],
muscleGroups: ['Back', 'Chest'],
difficulty: 'Intermediate'
},
{
title: 'Name',
type: 'Weights',
style: ['Strength Building'],
muscleGroups: ['Back', 'Chest'],
difficulty: 'Intermediate'
}
]
I would like to match exercises with data/preferences object. Here's a function but unfortunately I was only able to hardcode it and it doesn't work as expected. I need to compare data from this.generatorData with exercises - loop through all exercises and find these that match the requirements. Is there a way to make it work and if it is possible how do I automatise this function?
match() {
let categories = Object.values(this.generatorData)
for(let category of categories) {
if(category.length > 1) {
this.exercises.filter(exercise => {
if(exercise.type === category[0].name || exercise.type === category[1].name || exercise.type === category[2].name) {
if(exercise.difficulty === category[categories.length - 1].name) {
this.matchedExercies.push(exercise)
}
}
})
}
else if(category.length === 1) {
let filtered = this.exercises.filter(exercise => exercise.type === category[0].name)
console.log(filtered)
this.matchedExercies = filtered
}
}
}
Here's a codeSandbox.
so this is a matter of plain js, not so much vue.
Assuming the filtering is done using AND across filters and OR across filter choices, here is a working version (requiring some changes to the schema)
// the values are in an array, to use the `title` some changes may be needed
const generatorData = {
types: [],
style: [],
muscleGroups: [{name:'Back'}],
difficulty: [{name:'Beginner'},{name:'Intermediate'}]
}
const exercises = [
{
title: 'Name',
type: 'Weights',
style: ['Strength Building'],
muscleGroups: ['Toes', 'Chest'],
difficulty: 'Intermediate'
},
{
title: 'Name',
type: 'Weights',
style: ['Strength Building'],
muscleGroups: ['Back', 'Chest'],
difficulty: 'Intermediate'
},
{
title: 'Name',
type: 'Weights',
style: ['Strength Building'],
muscleGroups: ['Back', 'Chest'],
difficulty: 'Effin Hard'
}
]
// I loop over the categories first, removing any that are not needed
const categories = Object.keys(generatorData).map(k => {
// if you want to keep using `title`, this is a good place to do that (val from all titles)
if (generatorData[k].length > 0) return { key: k, val: generatorData[k].map(g => g.name) };;
return false
}).filter(i => i !== false);
let filtered = exercises.filter(e => {
// filter all categories, and compare length of matching filters with the number of filters (enforces AND rule)
return categories.filter(f => {
// if key is missing, assume true
if (e[f.key] === undefined) return true;
// loop through filter values and make sure at leas one matches (OR condition)
return f.val.filter(v => {
// handle string as direct match
if (typeof e === "string") return e[f.key] === v;
// handle array with `includes`
return e[f.key].includes(v)
}).length > 0
}).length === categories.length;
})
console.log(filtered)
update
Looking at the codesandbox, it looks like your store is actually providing the generatorData with name instead of title
instead of:
difficulty: [
{ title: 'Beginner', ..some properties },
{ title: 'Intermediate', .. }
]
it uses:
difficulty: [
{ name: 'Beginner'},
{ name: 'Intermediate'}
]
the code was updated to use array of objects with name
This can be better for complex selections.
matchPreferencesWithExercises() {
let categories = this.generatorData;
this.exercises.map(exercise => {
let error = 0;
let matched;
for (let categoryName in categories) {
if (exercise[categoryName]) {
if (typeof exercise[categoryName] === "string") {
!categories[categoryName]
.map(item => item.name)
.includes(exercise[categoryName]) && error++;
} else {
matched = 0;
exercise[categoryName].map(exerciseAttr => {
categories[categoryName].includes(exerciseAttr) && matched++;
});
}
}
}
(error === 0 || matched > 0) && this.matchedExercies.push(exercise);
});
}
https://codesandbox.io/s/qo74o6z9