Сократить несколько объектов в один, с максимальным значением и количеством - PullRequest
0 голосов
/ 16 октября 2018

У меня есть массив таких объектов:

    [
        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 3,
            collectTime: "15-OCT-2018 09:03:02",
            status: "unfinished"
        },

        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 2,
            collectTime: "14-OCT-2018 05:63:42",
            status: "unfinished"
        },

        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 5,
            collectTime: "15-OCT-2018 10:56:35",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 7,
            collectTime: "11-OCT-2018 13:12:41",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 10,
            collectTime: "15-OCT-2018 09:03:02",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 4,
            collectTime: "15-OCT-2018 22:36:32",
            status: "unfinished"
        },
    ]

Однако я не могу получить массив, который выглядит следующим образом:

    [
        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 5,  // highest occurrences value for the unique combination of name and mainName
            collectTime: "15-OCT-2018 10:56:35",  // collectTime corresponding to the highest occurrences
            finished: 1, // count of the status 
            unfinished: 2 // count of the status 
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 10, // highest occurrences value for the unique combination of name and mainName
            collectTime: "15-OCT-2018 09:03:02",  // collectTime corresponding to the highest occurrences
            finished: 2, // count of the status 
            unfinished: 1 // count of the status 
        },

    ]

Я не могу понятькак именно вы используете Array.prototype.reduce (), чтобы получить то, что мне нужно.Я смог добиться определенного прогресса, но не могу получить точный результат.Любое руководство с благодарностью, спасибо!

Ответы [ 5 ]

0 голосов
/ 16 октября 2018

Уменьшите до Object с ключом name в качестве ключей, используйте Object.values, чтобы получить Array результирующих значений (объектов). Примечание : во фрагменте getValues служит только для запуска с действительным кодом редуктора.

const grouped = Object.values(
  getValues().reduce( (collection, value) => {
    const isFinished = value.status === "finished";
    const current = collection[value.name];
     if (!current) {
       collection[value.name] = { ...value,
        finished: +(isFinished), 
        unfinished: +(!isFinished) };
       delete collection[value.name].status;
     } else {
       collection[value.name] = { ...current,
        occurrences: Math.max(value.occurrences, current.occurrences),
        collectTime: new Date(current.collectTime) < new Date(value.collectTime) 
          ? value.collectTime 
          : current.collectTime ,
        finished:  +(isFinished) + current.finished,
        unfinished: +(!isFinished) + current.unfinished  };
     }
     return collection;
  }, {} )
);

console.log(grouped);

function getValues() {
  return [
    {
        name: "aaa",
        mainName: "bbb",
        occurrences: 3,
        collectTime: "15-OCT-2018 09:03:02",
        status: "unfinished"
    },

    {
        name: "aaa",
        mainName: "bbb",
        occurrences: 2,
        collectTime: "14-OCT-2018 05:63:42",
        status: "unfinished"
    },

    {
        name: "aaa",
        mainName: "bbb",
        occurrences: 5,
        collectTime: "15-OCT-2018 10:56:35",
        status: "finished"
    },

    {
        name: "ccc",
        mainName: "ddd",
        occurrences: 7,
        collectTime: "11-OCT-2018 13:12:41",
        status: "finished"
    },

    {
        name: "ccc",
        mainName: "ddd",
        occurrences: 10,
        collectTime: "15-OCT-2018 09:03:02",
        status: "finished"
    },

    {
        name: "ccc",
        mainName: "ddd",
        occurrences: 4,
        collectTime: "15-OCT-2018 22:36:32",
        status: "unfinished"
    },
  ];
}
0 голосов
/ 16 октября 2018

Это - как вы упомянули - классическая проблема .reduce, но в этом случае ваша заметка будет иметь две сущности.Поскольку вам нужно отслеживать множество различных объектов и объединять их, я бы предложил сохранить его как объект объектов (благодаря этому доступ к объекту будет быстрее).Использование объекта объектов дешевле с вычислительной точки зрения, чем использование другого map из find или любых других операций с массивами.

Следовательно, ваш memo для сокращения может выглядеть так:

{
  'aaa': { 
     /* aaa content here */
  }, 
  'ccc': {
    /* ccc content here */
  }
}

Затем, на втором этапе, вы можете стандартизировать объект (преобразовать его в массив).Таким образом, вам не придется перебирать массив memo каждый раз при уменьшении.

Я буду предполагать, что name и mainName всегда происходят одинаково.Если существует несколько комбинаций, вам необходимо настроить «сводные» ключи, чтобы они были уникальными (например, memo[`${element.name}_${element.mainName}`] - я использовал строки шаблона для ясности);

1.Итерируйте исходный массив, чтобы уменьшить его."summary"

const result = myArray.reduce((memo, element) => {
  const uniqueKey = `${element[name]}_${element.mainName}`;
  // Initialize new memo key, if not available yet
  if (!memo[uniqueKey]) {
    memo[uniqueKey] = {
       name: element.name,
       mainName: element.mainName,
       collectTime: element.collectTime,
       occurrences: element.occurences,
       finished: element.status === 'finished' ? 1 : 0,
       unfinished: element.status === 'unfinished' ? 1 : 0,
    };
  }

  // I assume there are only 2 statuses here available
  if (element.status === 'finished') {
    memo[uniqueKey].finished = memo.finished + 1;
  } else {
    memo[uniqueKey].unfinished = memo.unfinished + 1;
  }

  // Increase occurences, if needed
  if (memo[uniqueKey].occurences < element.occurences) {
    memo[uniqueKey].occurences = element.occurences;
    memo[uniqueKey].collectTime = element.collectTime;
  }
}, {});

2. Преобразовать заметку в массив.Обратите внимание, что заметка довольно короткая, поэтому конверсия дешевая:

const newArray = Object.values(result);
0 голосов
/ 16 октября 2018

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

const arr = [
  {
    name: "aaa",
    mainName: "bbb",
    occurrences: 3,
    collectTime: "15-OCT-2018 09:03:02",
    status: "unfinished"
  },

  {
    name: "aaa",
    mainName: "bbb",
    occurrences: 2,
    collectTime: "14-OCT-2018 05:63:42",
    status: "unfinished"
  },

  {
    name: "aaa",
    mainName: "bbb",
    occurrences: 5,
    collectTime: "15-OCT-2018 10:56:35",
    status: "finished"
  },

  {
    name: "ccc",
    mainName: "ddd",
    occurrences: 7,
    collectTime: "11-OCT-2018 13:12:41",
    status: "finished"
  },

  {
    name: "ccc",
    mainName: "ddd",
    occurrences: 10,
    collectTime: "15-OCT-2018 09:03:02",
    status: "finished"
  },

  {
    name: "ccc",
    mainName: "ddd",
    occurrences: 4,
    collectTime: "15-OCT-2018 22:36:32",
    status: "unfinished"
  },
];

// Reduce the array
const res = arr.reduce((current, next) => {
  // Check whether the next item exists in the accumulator (current).
  const needle = current.find(i => i.name === next.name && i.mainName === next.mainName);
  // If it does...
  if (needle) {
    // increase the existing occurrences.
  	needle.occurrences += +next.occurrences;
    // increase the status counter according to the next status.
    needle[next.status] += 1;
    // replace the collectTime if needed.
    needle.collectTime = next.occurrences > needle.highestOccurrences ? next.collectTime : needle.collectTime;
    // Keep track of the highest occurrence found so far.
    needle.highestOccurrences = next.occurrences > needle.highestOccurrences ? next.occurrences : needle.highestOccurrences;
  }
  // Otherwise..
  else {
    // Create a "finished" property, and set it to 0.
  	next.finished = 0;
    // Create an "unfinished" property, and set it to 0.
    next.unfinished = 0;
    // Keep track of the highest occurrence for that item.
    next.highestOccurrences = next.occurrences;
    // Increase the status of that item accordingly.
    next[next.status] += 1;
    // Push this item to the accumulator.
  	current.push(next);
  }
  return current;
}, []).map(function(item){
  // Once done, just remove the undesired / unneeded properties.. BEWARE: this is unnecessary.
  delete item.highestOccurrences;
  delete item.status;
  return item;
});
console.log(res);

Объяснение находится непосредственно в коде.

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

0 голосов
/ 16 октября 2018

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

var data = [{ name: "aaa", mainName: "bbb", occurrences: 3, collectTime: "15-OCT-2018 09:03:02", status: "unfinished" }, { name: "aaa", mainName: "bbb", occurrences: 2, collectTime: "14-OCT-2018 05:63:42", status: "unfinished" }, { name: "aaa", mainName: "bbb", occurrences: 5, collectTime: "15-OCT-2018 10:56:35", status: "finished" }, { name: "ccc", mainName: "ddd", occurrences: 7, collectTime: "11-OCT-2018 13:12:41", status: "finished" }, { name: "ccc", mainName: "ddd", occurrences: 10, collectTime: "15-OCT-2018 09:03:02", status: "finished" }, { name: "ccc", mainName: "ddd", occurrences: 4, collectTime: "15-OCT-2018 22:36:32", status: "unfinished" }],
    result = data.reduce((r, { name, mainName, occurrences, collectTime, status }) => {
        var temp = r.find((o) => o.name === name);
        if (temp) {
            if (occurrences > temp.occurrences) {
                temp.occurrences = occurrences;
                temp.collectTime = collectTime;
            }
            temp[status]++;
        } else {
            r.push({
                name,
                mainName,
                occurrences,
                collectTime,
                finished: +(status === 'finished'),
                unfinished: +(status === 'unfinished') });
        }
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 16 октября 2018

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

var input =     [
        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 3,
            collectTime: "15-OCT-2018 09:03:02",
            status: "unfinished"
        },

        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 2,
            collectTime: "14-OCT-2018 05:63:42",
            status: "unfinished"
        },

        {
            name: "aaa",
            mainName: "bbb",
            occurrences: 5,
            collectTime: "15-OCT-2018 10:56:35",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 7,
            collectTime: "11-OCT-2018 13:12:41",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 10,
            collectTime: "15-OCT-2018 09:03:02",
            status: "finished"
        },

        {
            name: "ccc",
            mainName: "ddd",
            occurrences: 4,
            collectTime: "15-OCT-2018 22:36:32",
            status: "unfinished"
        },
    ]
    
  const output = input.reduce((arr, obj) => {
    let existing = arr.filter(t => t.name == obj.name && t.mainName == obj.mainName)[0]

    if(existing){
      if(obj.occurrences > existing.occurrences) {
        Object.assign(existing, obj)
      }
      if(existing[obj.status]) {
        existing[obj.status] += 1
      } else {
        existing[obj.status] = 1
      }
      delete existing.status
    } else {
      obj[obj.status] = 1
      delete obj.status
      arr.push(obj)
    }
    
    return arr
  }, [])
  
  console.log(output)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...