#javascript
#javascript
Вопрос:
У меня возникли небольшие проблемы с написанием сценария.
У меня есть структура курса, которая состоит из нескольких модулей, и в каждом модуле может быть несколько уроков.
Когда пользователь заходит на страницу, необходимо определить, какой урок будет выбран для его посещения.
Бизнес-правило таково: выберите первый урок, который не был просмотрен, и модуль, к которому принадлежит этот урок. Если все они просмотрены, выберите первый урок первого урока.
const course = {
title: 'Course name',
duration: '1h 30min',
modules: [
{
title: 'first module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: true
}
]
},
{
title: 'second module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: false
}
]
},
{
title: 'third module',
lessons: [
{
title: 'lesson 1',
viewed: false
},
{
title: 'lesson 2',
viewed: false
}
]
}
]
}
const selectedModule = {}
const selectedLesson = {}
я попробовал что-то вроде
course.modules.find((module) => {
return module.lessons.find((lesson) => {
if (!lesson.viewed) {
selectedModule = module
selectedLesson = lesson
return true
} else {
return false
}
})
})
но это не похоже на хорошее решение, кроме того, что я не делаю, catch
если find
сбой, затем выберите первый урок из первого модуля.
Что я могу попробовать дальше?
Комментарии:
1. рекурсивно перебирайте свой объект и возвращайте урок, если он соответствует вашим критериям, или вызывайте следующий, если он не соответствует
2. Я не понимаю, какое отношение все это имеет к break или catch?
3.
find
возвращает объект, не используйте его для установки таких свойств, какselectedModule
. Просто верните их4. @Liam если найдется урок, соответствующий критериям, следует остановиться. если не найдено, введите catch
5. Что плохого в простом использовании
if
инструкции? Действительно, очень непонятно, что именно вы пытаетесь здесь сделать.
Ответ №1:
Ваше решение могло бы сработать, но это не лучшее, что можно сделать. Вам просто нужно установить значения по умолчанию в начале.
const course = {
title: 'Course name',
duration: '1h 30min',
modules: [{
title: 'first module',
lessons: [{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: true
}
]
},
{
title: 'second module',
lessons: [{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: false
}
]
},
{
title: 'third module',
lessons: [{
title: 'lesson 1',
viewed: false
},
{
title: 'lesson 2',
viewed: false
}
]
}
]
}
let selectedModule = course.modules[0]
let selectedLesson = selectedModule.lessons[0]
course.modules.find((module) => {
return module.lessons.find((lesson) => {
if (!lesson.viewed) {
selectedModule = module
selectedLesson = lesson
return true
} else {
return false
}
})
})
console.log(selectedModule.title, selectedLesson.title);
В вашем коде вы пытаетесь сделать слишком много за один раз. Вам нужно разбить его на цикл for, из которого вы можете выйти, и find, который будет искать непросмотренные уроки. У вас была правильная идея, просто не используйте find() для установки переменных.
const course = {
title: 'Course name',
duration: '1h 30min',
modules: [
{
title: 'first module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: true
}
]
},
{
title: 'second module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: false
}
]
},
{
title: 'third module',
lessons: [
{
title: 'lesson 1',
viewed: false
},
{
title: 'lesson 2',
viewed: false
}
]
}
]
}
// set the default to first module and lesson
let selectedModule = course.modules[0];
let selectedLesson = selectedModule.lessons[0];
// loop over modules
for (const module of course.modules) {
// look for lesson not viewed
const unviewedLession = module.lessons.find(lesson => !lesson.viewed)
// if new lesson, use that and exit
if (unviewedLession) {
selectedModule = module;
selectedLesson = unviewedLession;
break;
}
}
console.log(selectedModule.title, selectedLesson.title);
Ответ №2:
Я бы сделал так:
-
Найдите первый неполный модуль, т.Е. модуль, в котором не просмотрен хотя бы один урок.
-
Получите непросмотренный урок из соответствующего модуля.
-
Если все просмотрены, найдите первый модуль, содержащий хотя бы один урок, получите из него первый урок.
let x, y;
const incompleteModule = course.modules.find(module => module.lessons amp;amp; module.lessons.length amp;amp; module.lessons.some(({viewed}) => !viewed));
if (incompleteModule) {
x = incompleteModule;
y = incompleteModule.lessons.find(({viewed}) => !viewed);
} else {
x = course.modules.find(module => module.lessons.length);
y = x.lessons[0];
}
Вывод для ваших выборочных данных:
{title: "second module", lessons: Array(2)}
{title: "lesson 2", viewed: false}
Когда для всего установлено значение viewed: true
{title: "first module", lessons: Array(2)}
{title: "lesson 1", viewed: true}
Комментарии:
1. Нет, у вас проблема с логикой. Вы использовали some там, где этого не должно быть some
2. «incompleteModule» — это логическое значение в вашем коде. Запустив его, вы будете иметь
"Uncaught TypeError: Cannot read property 'find' of undefined",
Ответ №3:
Попробуйте что-нибудь вроде этого
var module_and_lesson_loaded =
(course.modules.length>0 amp;amp; course.modules.find((module))?
course.modules.find((module) => {
return (module.lessons.length>0 amp;amp; module.lessons.find((lesson))?
module.lessons.find((lesson) => {
if (!lesson.viewed) {
selectedModule = module
selectedLesson = lesson
return true
} else {
return false
}
}):false;
}):false;
if(module_and_lesson_loaded){
//your code here
}else{
selectedModule = course.modules[0]
selectedLesson = selectedModule.lessons[0]
}
Ну, я думаю, это может быть неправильный способ, но он проверит, не являются ли эти массивы пустыми, и вернет значение null, если они есть.
Ответ №4:
Учитывая ваше бизнес-правило:
выберите первый урок, который не был просмотрен, и модуль, к которому принадлежит этот урок. Если все они просмотрены, выберите первый урок первого урока.
Редуктор здесь работает идеально:
// Loop through all modules and perform logic to determine the result
const result = course.modules.reduce((firstUnviewedLesson, currentModule) => {
// If we already found the first unviewed lesson, return it
if (firstUnviewedLesson) return firstUnviewedLesson
// Attempt to find an unviewed lesson within the module
const unviewedLesson = currentModule.lessons.find(lesson => lesson.viewed === false)
// If it is found, create our custom object stating the module name and the title of the lesson
if (unviewedLesson) {
console.log('Found unviewed lesson')
return {
moduleTitle: currentModule.title,
unviewedLesson: unviewedLesson.title
}
}
}, null)
После выполнения этой логики вы можете проверить, result === null
— в данном случае это означает, что все уроки были просмотрены, и вы можете выполнить вторую часть своей бизнес-логики.
Ответ №5:
const course = {
title: 'Course name',
duration: '1h 30min',
modules: [
{
title: 'first module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: true
}
]
},
{
title: 'second module',
lessons: [
{
title: 'lesson 1',
viewed: true
},
{
title: 'lesson 2',
viewed: false
}
]
},
{
title: 'third module',
lessons: [
{
title: 'lesson 1',
viewed: false
},
{
title: 'lesson 2',
viewed: false
}
]
}
]
}
const isEmpty = obj => {
return (Object.keys(obj).length === 0 amp;amp; obj.constructor === Object);
}
const findLesson = course => {
let selected = {};
course.modules.every(module => {
if(!isEmpty(selected)) return;
module.lessons.every(lesson => {
if (lesson.viewed === false) {
selected = {module: module.title, lesson: lesson.title};
return;
}
});
});
return (isEmpty(selected)) ? {lesson: "lesson1", module: "first module"} : selected;
};
console.log(findLesson(course));
Комментарии:
1. Он чистый, единственная критика заключается в том, что он повторяется больше раз, чем нужно, поскольку нет простого выхода из forEach. НО он выбирает неправильный модуль / урок, поскольку вы продолжаете проверять.
2. вы правы, я переработал его, чтобы использовать .every вместо forEach
Ответ №6:
Следующий подход намеренно использует дважды Array.prototype.some
. Таким образом, это, скорее всего, единственный подход, основанный на методах массива, который повторяет наименьшее количество возможных шагов. Он немедленно завершает оба условия одновременно, как только был найден первый непросмотренный урок.
Необходимая информация о модуле собирается с помощью объекта, который передается как this
контекст обоих методов обратного вызова …
const course = {
title: 'Course name',
duration: '1h 30min',
modules: [{
title: 'first module',
lessons: [{
title: 'lesson 1',
viewed: true
}, {
title: 'lesson 2',
viewed: true
}]
}, {
title: 'second module',
lessons: [{
title: 'lesson 1',
viewed: true
}, {
title: 'lesson 2',
viewed: false
}]
}, {
title: 'third module',
lessons: [{
title: 'lesson 1',
viewed: false
}, {
title: 'lesson 2',
viewed: false
}]
}]
};
function assignModuleData(data, module, idx) {
data.moduleTitle = module.title;
data.moduleIdx = idx;
data.module = module;
}
function assignLessonData(data, lesson, idx) {
data.lessonTitle = lesson.title;
data.lessonIdx = idx;
data.lesson = lesson;
}
function assignDefaultLesson(data, modules) {
assignModuleData(data, modules[0], 0)
assignLessonData(data, modules[0].lessons[0], 0)
}
function isUnviewedLessonAndWhichIsIt(lesson, idx) {
const data = this;
const isUnviewedLesson = !lesson.viewed;
if (isUnviewedLesson) {
assignLessonData(data, lesson, idx)
}
return isUnviewedLesson;
}
function isModuleWithUnviewedLessonAndWhichIsIt(module, idx, arr) {
const data = this;
assignModuleData(data, module, idx);
const isModuleWithUnviewedLesson = module.lessons.some(isUnviewedLessonAndWhichIsIt, data);
if ((idx >= (arr.length - 1)) amp;amp; !isModuleWithUnviewedLesson) {
assignDefaultLesson(data, arr);
}
return isModuleWithUnviewedLesson;
}
const nextLesson = {}
course.modules.some(isModuleWithUnviewedLessonAndWhichIsIt, nextLesson);
console.log(`"${ nextLesson.moduleTitle }, ${ nextLesson.lessonTitle }"`);
console.log(' nextLesson :', nextLesson);
.as-console-wrapper { min-height: 100%!important; top: 0; }