JavaScript Лучшая практика фильтрации массивов по трем факторам - PullRequest
1 голос
/ 23 февраля 2020

Я фильтрую массив транзакций Vet по следующим критериям:

  • Для каждой транзакции в течение часа в результат помещается только самая дорогая транзакция (транзакция: {собака, отметка времени, сумма})
  • Если более одной транзакции с одинаковыми связями для самой дорогой транзакции за один час, поместите только самую раннюю транзакцию в результат
  • Если их больше 10 транзакции для собаки в общем массиве транзакций, не включают в себя ни одной транзакции с этой собакой в ​​результате

Часовой период 00:00:00 - 00:59:59, 01 : 00: 00 - 01:59:59 и т. Д. c.

. Принимая во внимание сложность, я хотел бы найти более простое для чтения решение, которое следует передовым методам. Вот данные (уже отсортированные по времени):

const dogs = [
  { "dog":"ralph", "timestamp":"2/23/2020 03:04:57", "amount": 140.00 },
  { "dog":"toto", "timestamp":"2/23/2020 03:14:31", "amount": 130.00 },
  { "dog":"toto", "timestamp":"2/23/2020 03:15:10", "amount": 145.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 03:15:53", "amount": 175.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 04:05:44", "amount": 220.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 05:34:41", "amount": 100.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 05:39:11", "amount": 40.00 },
  { "dog":"toto", "timestamp":"2/23/2020 05:43:00", "amount": 240.00 },
  { "dog":"toto", "timestamp":"2/23/2020 05:59:58", "amount": 235.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 06:11:52", "amount": 20.00 },
  { "dog":"toto", "timestamp":"2/23/2020 06:12:53", "amount": 90.00 },
  { "dog":"rex", "timestamp":"2/23/2020 06:12:53", "amount": 315.00 },
  { "dog":"max", "timestamp":"2/23/2020 06:12:53", "amount": 285.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 06:13:14", "amount": 240.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 07:05:21", "amount": 60.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 08:42:50", "amount": 80.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 09:07:53", "amount": 100.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 10:07:35", "amount": 200.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 11:04:20", "amount": 120.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:04:40", "amount": 160.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 11:04:54", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:34:33", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:44:23", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:48:43", "amount": 125.00 },
  { "dog":"bella", "timestamp":"2/23/2020 12:03:53", "amount": 80.00 },
  { "dog":"bella", "timestamp":"2/23/2020 12:04:03", "amount": 100.00 },
  { "dog":"bella", "timestamp":"2/23/2020 13:11:54", "amount": 125.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 14:04:35", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 14:21:10", "amount": 170.00 },
  { "dog":"bella", "timestamp":"2/23/2020 15:15:18", "amount": 140.00 },
  { "dog":"bella", "timestamp":"2/23/2020 16:15:20", "amount": 180.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 17:49:55", "amount": 180.00 }
]

Вот мое рабочее решение:

function lessThanTen(dogs) {
  let count = {}
  let results = [];
  for(let i = 0; i<dogs.length; i++) {
    count[dogs[i].dog] ? count[dogs[i].dog] +=1 : count[dogs[i].dog] = 1;
  }
  for(let i = 0; i<dogs.length; i++) {
    if(!(count[dogs[i].dog] > 10)) {
      results.push(dogs[i]);
    }
  }
  return results;
}

function mostExpensive(dogs) {
  let curHour, nextHour, prevAmount, curAmount, nextAmount, highIndex;
  let results = [];
  const filtered = lessThanTen(dogs);

  filtered.forEach((click, index) => {
    curHour = filtered[index].timestamp.split(" ")[1].substring(0,2);
    curAmount = filtered[index].amount;
    if(index > 0) {
      prevAmount = filtered[index-1].amount;
    }
    if(index < filtered.length - 1) {
      nextHour = filtered[index + 1].timestamp.split(" ")[1].substring(0,2);
      nextAmount = filtered[index + 1].amount
    }
    if ((curHour === nextHour) && ((curAmount > prevAmount && curAmount > nextAmount) || (curAmount === nextAmount && !highIndex)) ) {
      highIndex = index;
    }
    if (nextHour > curHour) {
      results.push(filtered[highIndex ? highIndex : index]);
      highIndex = null;
    }
  });
  console.log(results);
}
mostExpensive(dogs);

Можно / нужно разорвать «если более одной транзакции для одной собаки через час "в свою собственную функцию, чтобы ее было проще тестировать?

Есть ли хороший способ очистить все операторы if в forEach? Я сделал удар по фильтру и уменьшению, но, к сожалению, заблудился при сравнении предыдущих и текущих значений и времени.

Должен ли я использовать что-то другое, чем для циклов? Какова лучшая практика здесь? Я не мог придумать, как избежать использования двух циклов O (2n). Предложения?

Каков наиболее разборчивый и функциональный способ выполнения sh трех критериев?

1 Ответ

0 голосов
/ 23 февраля 2020

lessThanTen

Вы неправильно используете условный оператор для работы в качестве оператора if. Выписанный (и использующий оператор in для проверки, входит ли собака в коллекцию count), вместо этого он будет:

if (dogs[i].dog in count) {
  count[dogs[i].dog] += 1;
} else {
  count[dogs[i].dog] = 1;
}

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

let dog = dogs[i].dog;
count[dog] = (dog in count ? count[dog] : 0) + 1;

Вместо добавления элементов к results в oop, вы можете использовать метод filter массива чтобы получить предметы, где собака встречается десять или меньше раз:

dogs.filter(item => count[item.dog] <= 10)

Это сделало бы функцию:

function lessThanTen(dogs) {
  let count = {};
  for (let i = 0; i < dogs.length; i++) {
    let dog = dogs[i].dog;
    count[dog] = (dog in count ? count[dog] : 0) + 1;
  }
  return dogs.filter(item => count[item.dog] <= 10);
}

mostExорого

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

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

function mostExpensive(dogs) {
  const filtered = lessThanTen(dogs);

  let results = [];
  let prevHour = null, highIndex = null;

  filtered.forEach((item, index) => {
    let hour = item.timestamp.split(" ")[1].substring(0,2);
    // When the hour changes
    if (prevHour !== null && hour !== prevHour) {
      // Add best item found in previous hour
      results.push(filtered[highIndex]);
      // Reset index for the new hour
      highIndex = null;
    }
    // When first item in hour, or better than previously found
    if (highIndex === null || item.amount > filtered[highIndex].amount) {
      // Keep the index of the best item
      highIndex = index;
    }
    // Keep the hour for next iteration
    prevHour = hour;

  });
  // If there is a result from the last hour
  if (highIndex !== null) {
    // Add best item found in last hour
    results.push(filtered[highIndex]);
  }
  console.log(results);
}
...