Слить и сравнить

Я написал логику для объединения всех партий из переменной message2. Он объединит все партии, если есть повторяющееся имя партии (AA, BB), и вычислит строки.

var message2 = {
    Batches: [ 
        {Batch: "AA", Lines: 1 },
        {Batch: "BB", Lines: 2 },
        {Batch: "BB", Lines: 6 }
    ]
}

Стали:

[ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 8 } ]

Это делается методом reduce().

В цикле forEach он зацикливает все mergedBatches (после слияния) и сравнивает с пакетом в переменной Worker. Ему нужно будет найти то же имя пакета, если строка Worker больше, чем строка mergedBatches, а затем установить mergedBatches, чтобы она соответствовала строке Worker.

var message2 = {
    Batches: [ 
        {Batch: "AA", Lines: 1 },
        {Batch: "BB", Lines: 2 },
        {Batch: "BB", Lines: 6 }
    ]
}

var Worker = { 
    Batches: [
        {Batch: "AA", Lines: 2 },
        {Batch: "BB", Lines: 3 },
    ]
}

var mergedBatches = message2.Batches.reduce((acc, obj)=>{
    var existObj = acc.find(b => b.Batch === obj.Batch);

    if (existObj) {
      existObj.Lines += obj.Lines;
      return acc;
    }

    acc.push({Batch: obj.Batch, Lines: obj.Lines});
    return acc;
},[]);

mergedBatches.forEach((b) => {
    var workerBatch = Worker.Batches.find(wB => wB.Batch === b.Batch);
    if (b.Lines >= workerBatch.Lines) {
        b.Lines = workerBatch.Lines;
    }
});


console.info(mergedBatches)

Окончательный результат, работающий как ожидалось:

[ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 3 } ]

Есть ли способ реорганизовать этот код, чтобы сделать его читабельным или лучше?

Это довольно просто с такой библиотекой, как underscore / lodash. Вы пользуетесь одним из них?

tokland 01.08.2018 23:35

@tokland Я не хочу использовать какую-либо библиотеку.

I'll-Be-Back 01.08.2018 23:36

Невозможно оптимизировать при создании; использовать карту и просто добавлять строки, если ключ уже существует в obj? (это вы занимаетесь созданием?) Таким образом, вам не нужно будет использовать часть уменьшения

V. Sambor 01.08.2018 23:44

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

SudoKid 02.08.2018 00:09

@EmettSpeer Не более 20 наборов данных (объектов в массиве)

I'll-Be-Back 02.08.2018 00:10

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

SudoKid 02.08.2018 00:13

@EmettSpeer Меня беспокоит не проблема производительности. В основном есть способ рефакторинга этого кода (более чистый способ), а не повышения производительности.

I'll-Be-Back 02.08.2018 00:18

> if (workerBatch.Lines > b.Lines) - это желаемое состояние, правда?

amankkg 02.08.2018 00:19

@amankkg Да. [ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 8 } ] и после желаемого состояния становится: [ { Batch: 'AA', Lines: 1 }, { Batch: 'BB', Lines: 3 } ]

I'll-Be-Back 02.08.2018 00:20

@ Я-вернусь Я имею в виду, что в описании вы сказали обратное - if the Worker line is more then mergedBatches line then set mergedBatches to match the Worker line

amankkg 02.08.2018 00:34
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
10
71
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это немного проще и должно быть быстрее:

const mergeBatches = (message) => {
  const obj = {};

  for (let i = message.Batches.length; i--;) {
    const current = message.Batches[i];

    if (current.Batch in obj) {
      obj[current.Batch] += current.Lines;
    } else {
      obj[current.Batch] = current.Lines;
    }
  }

  const arr = [];

  for (let key in obj) {
    arr.push({
      Batch: key,
      Lines: obj[key]
    })
  }

  return arr;
}

Это действительно хорошо, что вы изучаете функциональные шаблоны, но они не всегда самые быстрые.

Например, ваш код у вас acc.find. Под капотом find выполняет итерацию по массиву acc каждый раз, когда эта функция выполняется, что делает сложность O (n * n) Я думаю, что это, кто-нибудь прокомментирует, если я ошибаюсь.

В предоставленной мной функции вы выполняете итерацию по массиву Batches только один раз, что делает это O (n).

Спасибо. Отсутствует логика для проверки количества строк между переменными mergeBatches и Worker . См. if (b.Lines >= workerBatch.Lines) { } из моей темы.

I'll-Be-Back 02.08.2018 00:08

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

SudoKid 02.08.2018 00:08

@EmettSpeer Не более 20 объектов в массиве.

I'll-Be-Back 02.08.2018 00:09

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

SudoKid 02.08.2018 00:10

Исходя из вашей текущей структуры, это был бы еще один способ прийти к ожидаемому результату:

const merged = {};
message2.Batches.forEach(b => {
  if (merged[b.Batch]) {
    merged[b.Batch].Lines += b.Lines;
  } else {
    merged[b.Batch] = b;
  }
});

const result = [];
Worker.Batches.forEach(b => {
  if (merged[b.Batch] && merged[b.Batch].Lines > b.Lines) {
    merged[b.Batch].Lines = b.Lines;
  }
  result.push(merged[b.Batch]);
});

// Output
[{ "Batch": "AA", "Lines": 1 }, { "Batch": "BB", "Lines": 3 }]
Ответ принят как подходящий

Вот более короткая версия:

  1. если mergedBatches не должен содержать ссылок на записи message2.Batches, вы можете использовать деструктурирование: acc.push({ ...cur });
  2. однострочный if/else должен быть более читабельным без скобок;
  3. нулевая проверка в последнем условии: find может вернуть undefined.

const message2 = {
  Batches: [ 
    {Batch: "AA", Lines: 1 },
    {Batch: "BB", Lines: 2 },
    {Batch: "BB", Lines: 6 }
  ]
}

const Worker = { 
  Batches: [
    {Batch: "AA", Lines: 2 },
    {Batch: "BB", Lines: 3 },
  ]
}

const mergedBatches = message2.Batches.reduce((acc, cur) => {
  const prev = acc.find(x => x.Batch === cur.Batch)

  if (prev) prev.Lines += cur.Lines
  else acc.push(cur)

  return acc
}, [])

mergedBatches.forEach((mb) => {
  const wb = Worker.Batches.find(x => x.Batch === mb.Batch)

  if (wb && wb.Lines < mb.Lines ) mb.Lines = wb.Lines
})

console.info(mergedBatches)

Другие вопросы по теме