У меня есть рабочее решение этой проблемы, но я стараюсь сделать ее более чистой и аккуратной. Я придумал другое решение, которое использует функцию в функции карты. К сожалению, в этой версии есть несколько проблем, и я просто хочу знать, почему второе решение не работает. Я предполагаю, что это проблема переменной области здесь. Я с нетерпением жду, чтобы узнать ваше мнение об этом.

У меня есть простая функция, которая печатает календарные дни в массиве!

Поэтому вопрос в том, почему первая версия моего кода дает ожидаемые результаты, а вторая версия выдает неожиданные результаты.

Я попытался изменить let на var, а также сделал counter и startedIndexing вне области действия функции.

Решение 1 (работает):

const currentFullMonth = {
   days_length: 31,
   first_day: "Thu",
   first_day_index: 4,
   last_day: "Sat",
   last_day_index: 6,
   month: "Aug",
   year: 2019
}

const testMonth = [
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0]
];

function printMonthCalender(month) {
    let counter = 0;
    let startedIdxing = false;
    return month.map(week => {
        return week.map((day, index) => {
            if (index === currentFullMonth.first_day_index && !startedIdxing) {
                counter++;
                startedIdxing = true;
                return counter;
            } else if (startedIdxing) {
                if (currentFullMonth.days_length === counter) {
                    counter = 0;
                }
                counter++;
                return counter;
            } else {
                return 0;
            }
        });
    });
} // end of Solution #1 <-- this works :)

Решение 2 (не работает):

// start of Solution #2 <-- does not work :(    
// im using two functions to make it look more cleaner
//
function printMonthCalender2(month) {
    let counter = 0;
    let startedIdxing = false;
    return month.map(week => {
        return week.map((day, index) =>
            indexingMonth(counter, startedIdxing, index)
        );
    });
}
function indexingMonth(counter, startedIdxing, index) {
    if (index === currentFullMonth.first_day_index && !startedIdxing) {
        counter++;
        startedIdxing = true;
        return counter;
    } else if (startedIdxing) {
        if (currentFullMonth.days_length === counter) {
            counter = 0;
        }
        counter++;
        return counter;
    } else {
        return 0;
    }
}// end of Solution #2

console.log(printMonthCalender(testMonth));
console.log(printMonthCalender2(testMonth));

Ожидаемый результат следующим образом (первая версия):

[0, 0, 0, 0, 1, 2, 3]
[4, 5, 6, 7, 8, 9, 10]
[11, 12, 13, 14, 15, 16, 17]
[18, 19, 20, 21, 22, 23, 24]
[25, 26, 27, 28, 29, 30, 31]
[1, 2, 3, 4, 5, 6, 7]

Неожиданный результат следующим образом (вторая версия):

[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
3
user2256465 19 Авг 2019 в 01:04

2 ответа

Лучший ответ

Проблема в том, что когда вы переназначаете startedIdxing внутри indexingMonth, это локальная переменная, поэтому она не изменяется внутри вызывающей функции (printMonthCalender2).

Проблема в том, что .map не должен иметь мутации или переназначения в качестве побочного эффекта. Хотя вы могли настраивать параметры таким образом, чтобы indexingMonth возвращал то, что вы проверили, а затем переназначали startedIdxing, я бы предпочел другой подход: создать плоский массив, например

[0, 0, 0, 0, 1, 2, ..., 30, 31, 1, 2, 3]

А затем разбить его на кусочки по 7 после:

const currentFullMonth = {
   days_length: 31,
   first_day: "Thu",
   first_day_index: 4,
   last_day: "Sat",
   last_day_index: 6,
   month: "Aug",
   year: 2019
}

const makeZeroArr = length => new Array(length).fill(0);
const printMonthCalendar = (testMonth) => {
  // Create array: [1, 2, 3, ..., 30, 31]
  const oneMonth = Array.from(
    { length: currentFullMonth.days_length },
    (_, i) => i + 1
  );
  // Create a flat array with leading zeros and trailing last week:
  // [0, 0, 0, 0, 1, 2, 3, ..., 30, 31, 1, 2, 3, 4, 5, 6, 7]
  const flatResultArr = [
    ...makeZeroArr(currentFullMonth.first_day_index),
    ...oneMonth,
    ...oneMonth // this includes extra numbers that will be trimmed
  ].slice(0, 7 * 6); // 7 days/week * 6 weeks
  // Chunk the flat array into slices of 7:
  const resultArr = [];
  for (let i = 0; i < 7; i++) {
    resultArr.push(flatResultArr.slice(i * 7, (i + 1) * 7));
  }
  return resultArr;
};

console.log(printMonthCalendar());
1
CertainPerformance 18 Авг 2019 в 22:23

В функциях примитивные типы, такие как числа и логические значения, передаются по значению, а не по ссылке. Поэтому, когда вы определяете counter и startedIdxing в printMonthCalender2, а затем пытаетесь измените их в indexingMonth, изменения будут потеряны, как только вы вернетесь к printMonthCalender2.

Однако в JavaScript объекты передаются по ссылке. Так что-то вроде этого будет работать:

function printMonthCalender2(month) {
  let obj = { counter: 0, startedIdxing = false };
  return month.map(week => {
    return week.map((day, index) =>
      indexingMonth(obj, index)
    );
  });
}
function indexingMonth(obj, index) {
  if (index === currentFullMonth.first_day_index && !obj.startedIdxing) {
    obj.counter++;
    obj.startedIdxing = true;
    return obj.counter;
  } else if (obj.startedIdxing) {
    if (currentFullMonth.days_length === obj.counter) {
      obj.counter = 0;
    }
    obj.counter++;
    return obj.counter;
  } else {
    return 0;
  }
}// end of Solution #2

Такие вещи, как obj.counter++, фактически сохранят эти изменения в вашем исходном объекте, определенном в printMonthCalender2.


Предупреждение. Хотя вы можете это сделать, если вы работаете со сложным кодом, это часто осуждается. Эти виды мутаций могут быть очень трудно отладить, если возникает проблема. Это законная методика программирования, но ею нельзя злоупотреблять.

Кроме того, если вы работаете в команде, которая придерживается парадигмы функционального программирования, я считаю, что это большой нет-нет.

Однако, учитывая очень короткую продолжительность и ограниченную область действия переменной obj в этом примере, я лично чувствую себя очень комфортно с этим. Если бы obj имел намного более длительный срок службы и использовался во многих местах кода, то я бы был более осторожен с этим и согласился бы с комментарием @ CertainPerformance о том, что выражение map не должно изменяться ,

1
David784 18 Авг 2019 в 22:23