@yaroslavklimuk

Вопрос с собеседования. Вывести сумму транзакций за период, используя один цикл?

Есть массив объектов, в которых хранятся даты транзакций и сумма. Вывести сумму транзакций за каждый месяц, например, с 25.01. по 24.02, с 25.02 по 24.03 и т.д.
Разрешено использовать только один цикл. Задавать параметров не нужно, период определен. Массив отсортирован. Нельзя использовать метод reduce и т.п.

Пример массива

const transactions = [{
	date: '25.01.2015',
	price: '28',
},
{
	date: '22.02.2015',
	price: '23',
},
{
	date: '24.02.2015',
	price: '12',
},
{
	date: '02.03.2015',
	price: '55',
},
{
	date: '10.03.2015',
	price: '56',
},
{
	date: '15.03.2015',
	price: '8',
},
{
	date: '24.03.2015',
	price: '19',
},
{
	date: '26.03.2015',
	price: '12',
},
{
	date: '27.03.2015',
	price: '11',
},
];

Таким образом привожу дату в нормальный вид для дальнейшего сравнения, но не могу правильно указать условие.

Код

function getDate(str) {
	const dataTokens = str.split('.');
	const date = new Date(+dataTokens[2], +dataTokens[1] - 1, +dataTokens[0]);
	return date;
}
for (let i = 0; i < transactions.length; i++) {
	const date = getDate(transactions[i].date);
	...
}

  • Вопрос задан
  • 221 просмотр
Решения вопроса 1
WblCHA
@WblCHA
Раз никто так и не смог ничего толком предложить, то пришлось самому писать. Писал тяп-ляп, так что есть куда улучшать.

Мой вариант
{
  const transactions = [
    {
      date: '25.01.2015',
      price: '28',
    },
    {
      date: '22.02.2015',
      price: '23',
    },
    {
      date: '24.02.2015',
      price: '12',
    },
    {
      date: '02.03.2015',
      price: '55',
    },
    {
      date: '10.03.2015',
      price: '56',
    },
    {
      date: '15.03.2015',
      price: '8',
    },
    {
      date: '24.03.2015',
      price: '19',
    },
    {
      date: '26.03.2015',
      price: '12',
    },
    {
      date: '27.03.2015',
      price: '11',
    },
  ];

  const firstMonthDate = 25;
  const firstTransaction = transactions[0].date.split('.').map((n) => +n);

  const increaseMonth = (date) => {
    date.month = ((date.month + 12) % 12) + 1;

    if (date.month === 1) {
      date.year++;
    }
  };

  const startDate =
    firstTransaction[0] < firstMonthDate
      ? {
          day: firstMonthDate,
          month: ((firstTransaction[1] + 10) % 12) + 1,
          year: firstTransaction[2] - (firstTransaction[1] === 1 ? 1 : 0),
        }
      : {
          day: firstMonthDate,
          month: firstTransaction[1],
          year: firstTransaction[2],
        };
  const endDate = { ...startDate };
  increaseMonth(endDate);
  endDate.day--;

  const sumByMonths = [
    {
      from: { ...startDate },
      to: { ...endDate },
      sum: 0,
    },
  ];
  let sumByMonthIndex = 0;

  transactions.forEach((transaction) => {
    const [day, month] = transaction.date.split('.').map((n) => +n);

    if (
      !(month === startDate.month && day >= startDate.day) &&
      !(month === endDate.month && day <= endDate.day)
    ) {
      increaseMonth(startDate);
      increaseMonth(endDate);

      sumByMonths.push({
        from: { ...startDate },
        to: { ...endDate },
        sum: 0,
      });
      sumByMonthIndex++;
    }

    sumByMonths[sumByMonthIndex].sum += +transaction.price;
  });

  console.log('>>> sumByMonths', sumByMonths);
}


Надим Закиров,
Вариант, основанный на предложении @zkrvndm
{
  const transactions = [
    {
      date: '25.01.2015',
      price: '28',
    },
    {
      date: '22.02.2015',
      price: '23',
    },
    {
      date: '24.02.2015',
      price: '12',
    },
    {
      date: '02.03.2015',
      price: '55',
    },
    {
      date: '10.03.2015',
      price: '56',
    },
    {
      date: '15.03.2015',
      price: '8',
    },
    {
      date: '24.03.2015',
      price: '19',
    },
    {
      date: '26.03.2015',
      price: '12',
    },
    {
      date: '27.03.2015',
      price: '11',
    },
  ];

  const firstMonthDate = 25;
  const firstTransaction = transactions[0].date.split('.').map((n) => +n);

  const increaseMonth = (date) => {
    date.month = ((date.month + 12) % 12) + 1;

    if (date.month === 1) {
      date.year++;
    }
  };

  const dateToString = (date) => `${date.year}-${date.month}-${date.day}`;

  const startDate =
    firstTransaction[0] < firstMonthDate
      ? {
          day: firstMonthDate,
          month: ((firstTransaction[1] + 10) % 12) + 1,
          year: firstTransaction[2] - (firstTransaction[1] === 1 ? 1 : 0),
          unix: 0,
        }
      : {
          day: firstMonthDate,
          month: firstTransaction[1],
          year: firstTransaction[2],
          unix: 0,
        };
  startDate.unix = +new Date(dateToString(startDate));

  const endDate = { ...startDate };
  increaseMonth(endDate);
  endDate.unix = +new Date(dateToString(endDate)) - 1;

  const sumByMonths = [
    {
      from: {
        day: startDate.day,
        month: startDate.month,
        year: startDate.year,
      },
      to: { day: endDate.day - 1, month: endDate.month, year: endDate.year },
      sum: 0,
    },
  ];
  let sumByMonthIndex = 0;

  transactions.forEach((transaction) => {
    const date = +new Date(transaction.date.split('.').reverse().join('-'));

    if (!(date >= startDate.unix && date <= endDate.unix)) {
      increaseMonth(startDate);
      startDate.unix = +new Date(dateToString(startDate));
      increaseMonth(endDate);
      endDate.unix = +new Date(dateToString(endDate)) - 1;

      sumByMonths.push({
        from: {
          day: startDate.day,
          month: startDate.month,
          year: startDate.year,
        },
        to: { day: endDate.day - 1, month: endDate.month, year: endDate.year },
        sum: 0,
      });
      sumByMonthIndex++;
    }

    sumByMonths[sumByMonthIndex].sum += +transaction.price;
  });

  console.log('>>> sumByMonths', sumByMonths);
}


Лично мне мой вариант для конкретно этого таска симпатичнее, как минимум нет постоянных преобразований дат.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
zkrvndm
@zkrvndm
Архитектор решений
Ваше задание с собеседования это не про умение работать с циклам, а скорее про умение сравнивать время.
1. Для начала приводите даты к виду ГГГГ-ММ-ДД это можно легко сделать используя методы split / reverse / join
2. Далее полученную дату скармливайте new Date и извлекайте из него Unix-время в миллисекундах (метод getTime)
3. Полученное время в миллисекундах уже можно легко сравнивать между с собой используя операторы сравнения
Ответ написан
@mexvod
const filteredArray = transactions.filter((item) => {
        const dataTokens = item.date.split('.');
        const date = new Date(+dataTokens[2], +dataTokens[1] - 1, +dataTokens[0]);
        return date.getDate() >= 24 && date.getDate() <= 25;
});
console.log(filteredArray);

Остальное, я думаю, уже поймете как доделать =)
советую почитать про https://learn.javascript.ru/array-methods

Второй вариант, максимально упрощенный, сумма по месяцам, конкретным:

let currentMonth = 0;
    let isFirstRun = true;
    let bufferSumInMonth = 0;
    const pricesArray = [];

    for (let i = 0; i < transactions.length; i++) {
        const currentItem = transactions[i];
        const dataTokens = currentItem.date.split('.');
        const date = new Date(+dataTokens[2], +dataTokens[1] - 1, +dataTokens[0]);
        if (isFirstRun) { currentMonth = date.getMonth(); isFirstRun = false; }
        if (currentMonth === date.getMonth() && (date.getDate() >= 24 && date.getDate() <= 25)) {
            bufferSumInMonth += parseInt(currentItem.price, 10);
        } else if (currentMonth !== date.getMonth() || i === transactions.length - 1) {
            console.log(bufferSumInMonth);
            pricesArray.push({ sum: bufferSumInMonth, month: date.getMonth() });
            bufferSumInMonth = 0;
        }
        currentMonth = date.getMonth();
    }

    console.log(pricesArray);
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы