Использование «Reduce» для манипулирования объектом и создания массива - PullRequest
0 голосов
/ 30 сентября 2019

Я понимаю, что Reduce - это очень мощный метод массива в Javascript, и я видел много примеров, но не смог использовать его для выполнения нижеприведенной задачи.

Группировать объект статистики людей по возрасту, гдеразница в возрасте не должна превышать 5, и в каждой группе должно быть только максимальное число 3.

Я смог добиться этого с кодом ниже

const immutable = require('../fixtures/inputs/input')
const edge = require('../fixtures/inputs/edge-input')
/**
 * This is the entry point to the program
 *
 * @param {any} input Array of student objects
 */


function classifier(input) {
    // console.log(input)
    // let returnedInput = []
    let newInput = JSON.parse(JSON.stringify(input));

    let exampleOutput = {

    }


    if (!Array.isArray(newInput)) {

        throw new Error("invalid")
    }
    if (newInput.length < 1) {
        exampleOutput = { noOfGroups: 0 }
    }
    function compare(a, b) {
        const { age: ageA, regNo: regNoA } = a
        const { age: ageB, regNo: regNoB } = b
        // const ageA = a.age
        // const ageB = b.age
        let comparison = 0;
        if (ageA > ageB) {
            comparison = 1;
        } else if (ageA < ageB) {
            comparison = -1;
        }
        return comparison
    }

    const ages = newInput.map(function (each) {
        let datDob = new Date(each.dob).getFullYear()
        return each.age = new Date().getFullYear() - datDob
    })

    sortedInput = newInput.sort(compare)
    // console.log(sortedInput)
    const getMember = (arg) => {
        let memArray = []
        // console.log(arg)
        if (arg.length == 1) {
            return arg
        }
        let i = 0;
        let j = 1;
        // console.log(arg)
        // console.log(arg.length)
        while (i <= arg.length) {

            while (j < 3) {
                //  console.log(arg[j])
                if (arg[j]) {
                    if ((arg[j].age - arg[i].age) <= 5) {

                        memArray.push(arg[j])
                    }
                }

                j++
            }

            memArray.push(arg[i])
            i++

            return memArray
        }

    }

    let i = 0;
    // console.log(sortedInput)

    while (sortedInput.length >= 1) {
        // console.log(sortedInput)
        let memberss = getMember(sortedInput)
        memberss = memberss.sort(compare)
        // let memRegSort = memberss.sort((a, b) => (a.regNo > b.regNo) ? 1 : -1) 
        memberss = memberss.sort((a, b) => (a.age > b.age) ? 1 : (a.age === b.age) ? ((a.regNo > b.regNo) ? 1 : -1) : -1)
        // return memberss
        const oldest = memberss.map(item => item.age).reduce((a, b) => Math.max(a, b))
        const sumAge = memberss.map(item => item.age).reduce((total, curVal) => total + curVal)
        const regNo = memberss.map(item => parseInt(item.regNo))
        exampleOutput[`noOfGroups`] = i + 1
        exampleOutput[`group${i + 1}`] = {}
        exampleOutput[`group${i + 1}`]['members'] = memberss
        exampleOutput[`group${i + 1}`].oldest = oldest
        exampleOutput[`group${i + 1}`].sum = sumAge

        exampleOutput[`group${i + 1}`].regNos = regNo.sort((a, b) => a > b ? 1 : -1)
        sortedInput = sortedInput.slice(memberss.length, sortedInput.length + 1)
        // console.log(sortedInput)
        // sortedInput.splice(0, memberss.length)
        // console.log(exampleOutput[`group${i + 1}`]['members'])


        i++
    }

    // console.log(exampleOutput)
    return exampleOutput
    // console.log (getMember(sortedInput))

}
const input = [
    {
        name: 'Hendrick',
        dob: '1853-07-18T00:00:00.000Z',
        regNo: '041',
    }

]
Object.freeze(edge)
const out = classifier(edge)
console.log(out)

module.exports = classifier;

input

const input = [
  {
    name: 'Hendrick',
    dob: '1853-07-18T00:00:00.000Z',
    regNo: '041',
  },
  {
    name: 'Albert',
    dob: '1910-03-14T00:00:00.000Z',
    regNo: '033',
  },
  {
    name: 'Marie',
    dob: '1953-11-07T00:00:00.000Z',
    regNo: '024',
  },
  {
    name: 'Neils',
    dob: '1853-10-07T00:00:00.000Z',
    regNo: '02',
  },
  {
    name: 'Max',
    dob: '1853-04-23T00:00:00.000Z',
    regNo: '014',
  },
  {
    name: 'Erwin',
    dob: '1854-08-12T00:00:00.000Z',
    regNo: '09',
  },
  {
    name: 'Auguste',
    dob: '1854-01-28T00:00:00.000Z',
    regNo: '08',
  },
  {
    name: 'Karl',
    dob: '1852-12-05T00:00:00.000Z',
    regNo: '120',
  },
  {
    name: 'Louis', //
    dob: '1852-08-15T00:00:00.000Z',
    regNo: '022',
  },
  {
    name: 'Arthur',
    dob: '1892-09-10T00:00:00.000Z',
    regNo: '321',
  },
  {
    name: 'Paul',
    dob: '1902-08-08T00:00:00.000Z',
    regNo: '055',
  },
  {
    name: 'William',
    dob: '1890-03-31T00:00:00.000Z',
    regNo: '013',
  },
  {
    name: 'Owen',
    dob: '1853-04-26T00:00:00.000Z',
    regNo: '052',
  },
  {
    name: 'Martin',
    dob: '1854-02-15T00:00:00.000Z',
    regNo: '063',
  },
  {
    name: 'Guye',
    dob: '1854-10-15T00:00:00.000Z',
    regNo: '084',
  },
  {
    name: 'Charles',
    dob: '1954-02-14T00:00:00.000Z',
    regNo: '091',
  },
];

module.exports = input;

output

{ noOfGroups: 8,
  group1:
   { members:
      '[{"name":"Charles","dob":"1954-02-14T00:00:00.000Z","regNo":"091","age":65},{"name":"Marie","dob":"1953-11-07T00:00:00.000Z","regNo":"024","age":66}]',
     oldest: 66,
     sum: 131,
     regNos: [ 24, 91 ] },
  group2:
   { members:
      '[{"name":"Albert","dob":"1910-03-14T00:00:00.000Z","regNo":"033","age":109}]',     oldest: 109,
     sum: 109,
     regNos: [ 33 ] },
  group3:
   { members:
      '[{"name":"Paul","dob":"1902-08-08T00:00:00.000Z","regNo":"055","age":117}]',  
     oldest: 117,
     sum: 117,
     regNos: [ 55 ] },
  group4:
   { members:
      '[{"name":"Arthur","dob":"1892-09-10T00:00:00.000Z","regNo":"321","age":127},{"name":"William","dob":"1890-03-31T00:00:00.000Z","regNo":"013","age":129}]',
     oldest: 129,
     sum: 256,
     regNos: [ 13, 321 ] },
  group5:
   { members:
      '[{"name":"Auguste","dob":"1854-01-28T00:00:00.000Z","regNo":"08","age":165},{"name":"Guye","dob":"1854-10-15T00:00:00.000Z","regNo":"084","age":165},{"name":"Erwin","dob":"1854-08-12T00:00:00.000Z","regNo":"09","age":165}]',
     oldest: 165,
     sum: 495,
     regNos: [ 8, 9, 84 ] },
  group6:
   { members:
      '[{"name":"Martin","dob":"1854-02-15T00:00:00.000Z","regNo":"063","age":165},{"name":"Max","dob":"1853-04-23T00:00:00.000Z","regNo":"014","age":166},{"name":"Hendrick","dob":"1853-07-18T00:00:00.000Z","regNo":"041","age":166}]',
     oldest: 166,
     sum: 497,
     regNos: [ 14, 41, 63 ] },
  group7:
   { members:
      '[{"name":"Neils","dob":"1853-10-07T00:00:00.000Z","regNo":"02","age":166},{"name":"Owen","dob":"1853-04-26T00:00:00.000Z","regNo":"052","age":166},{"name":"Karl","dob":"1852-12-05T00:00:00.000Z","regNo":"120","age":167}]',
     oldest: 167,
     sum: 499,
     regNos: [ 2, 52, 120 ] },
  group8:
   { members:
      '[{"name":"Louis","dob":"1852-08-15T00:00:00.000Z","regNo":"022","age":167}]', 
     oldest: 167,
     sum: 167,
     regNos: [ 22 ] } }

Как я могу сделать то же самое, используя Reduce. Я попробовал код ниже

function classifier(input) {
    let newInput = JSON.parse(JSON.stringify(input));

    let exampleOutput = {
        noOfGroups:0,
        group: {
            member: [],
            oldest: 0,
            regNos: []
        }

    }
    if (!Array.isArray(newInput)) {

        throw new Error("invalid")
    }
    if (newInput.length < 1) {
        exampleOutput = { noOfGroups: 0 }
    }
    function compare(a, b) {
        const { age: ageA } = a
        const { age: ageB } = b
        return ageA-ageB
    }

    const ages = newInput.map(function (each) {
        let datDob = new Date(each.dob).getFullYear()
        return each.age = new Date().getFullYear() - datDob
    })

    sortedInput = newInput.sort(compare)

    const member = (arr)=>{
        let result = []
        return arr.length < 1 ? { noOfGroups: 0} : 
            arr.reduce((acc, cur, index, arr) => {
                index= index-1
                let num = 0
                // console.log(cur.age)
                let item = arr.findIndex(item => item.age +5 >= cur.age)
                item == 0 ? result.push(cur) : result

                result.length > 3 ? result.pop() : result
                num = num+1
                acc.noOfGroups = num
                acc[`group${num}`] = {}
                acc[`group${num}`].members = []
                acc[`group${num}`].members.push(result)
                acc[`group${num}`].oldest = result.map(item => item.age).reduce((a, b) => Math.max(a, b))
                acc[`group${num}`].regNos = result.map(item => item.age)
                // console.log(arr.slice)
                index = index-1
                return index < 0 ? member(arr.slice(acc[`group${num}`].regNos.length, 16)) : acc

                return acc


        }, [{noOfGroups: 0}, ])

    }

    return member(sortedInput)
    return exampleOutput


}

Но получил вывод для одной группы, например, так:

{ noOfGroups: 1,
  group1: { members: [ [Array] ], oldest: 66, regNos: [ 65, 66 ] } }

Ответы [ 2 ]

0 голосов
/ 30 сентября 2019

Сначала я написал это, используя Рамда (отказ от ответственности: я один из его авторов.) Это был код:

const transform = pipe (
  map (addAge),
  sortBy (prop ('age')),
  makeGroups (doesItFit),
  nameGroups,
  map (constructGroup)
)

с дополнительными вспомогательными функциями, addAge, makeGroups, doesItFit, nameGroups и constructObj). Мне действительно нравится этот стиль поэтапного преобразования выходных данных.

Но Ramda - это просто набор вспомогательных функций (разумеется, предназначенный для обеспечения определенного специфического стиля кодирования), и его легко написать. сами их версии.

Но, поместив такие функции, как

const pipe = (...fns) => (arg) => 
  fns.reduce((o, f) => f(o), arg)

, в наши собственные библиотеки утилит, мы сможем относительно легко создать такой же чистый код, как и приведенный выше. в этом случае я использовал несколько функций Ramda, а также двух помощников, которые могут принадлежать такой библиотеке (numericSort и makeGroups.). Но их легко написать для себя, и мы можем построить эту функциюповерх них.

// Utility functions
const pipe = (...fns) => (arg) => 
  fns.reduce((o, f) => f(o), arg)

const map = (fn) => (xs) => 
  xs .map (x => fn(x))

const sort = (comparator) => (xs) => 
  xs .sort (comparator)

const numericSort = sort ((a, b) => a - b)

const sortBy = (fn) => (xs) => 
  xs .sort ((a, b, x = fn(a), y = fn(b)) => x < y ? -1 : x > y ? 1 : 0)

const sum = (xs) =>
  xs.reduce((a, b) => a + b, 0)

const prop = (name) => (obj) =>
  obj [name]

const pluck = (prop) => (xs) =>
  xs .map (x => x[prop])

const mapObject = (fn) => (obj) => 
  Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: fn(v)})))

const makeGroups = (test) => (xs) =>
  xs.reduce((groups, x) => {
    const idx = groups.findIndex(g => test (g, x))
    return idx > -1 
      ? [...groups.slice(0, idx), [...groups[idx], x], ...groups.slice(idx + 1)]
      : [...groups, [x]]
  }, [])


// Helpers
const addAge = ({dob, ...rest}) =>  
  ({...rest, dob, age: new Date().getFullYear() - new Date(dob).getFullYear()})

const nameGroups = groups =>
  groups .reduce ((i => (a, g) => ({...a, [`group${i++}`]: g}))(1), {})

const doesItFit = (group, item) =>
  group.length < 3 && group.every(x => Math.abs(x.age - item.age) < 5)

const constructGroup = (group) => ({
  members: group,
  // members: JSON.stringify(group),
  oldest: Math.max(...pluck ('age') (group)),
  sum: sum (pluck ('age') (group)),
  regNos: numericSort (map(Number) (pluck('regNo') (group))),
})


// Main function
const transform = pipe (
  map (addAge),
  sortBy (prop ('age')),
  makeGroups (doesItFit),
  nameGroups,
  mapObject (constructGroup)
)


// Demonstration
const input = [{name: "Hendrick", dob: "1853-07-18T00:00:00.000Z", regNo: "041"}, {name: "Albert", dob: "1910-03-14T00:00:00.000Z", regNo: "033"}, {name: "Marie", dob: "1953-11-07T00:00:00.000Z", regNo: "024"}, {name: "Neils", dob: "1853-10-07T00:00:00.000Z", regNo: "02"}, {name: "Max", dob: "1853-04-23T00:00:00.000Z", regNo: "014"}, {name: "Erwin", dob: "1854-08-12T00:00:00.000Z", regNo: "09"}, {name: "Auguste", dob: "1854-01-28T00:00:00.000Z", regNo: "08"}, {name: "Karl", dob: "1852-12-05T00:00:00.000Z", regNo: "120"}, {name: "Louis", dob: "1852-08-15T00:00:00.000Z", regNo: "022"}, {name: "Arthur", dob: "1892-09-10T00:00:00.000Z", regNo: "321"}, {name: "Paul", dob: "1902-08-08T00:00:00.000Z", regNo: "055"}, {name: "William", dob: "1890-03-31T00:00:00.000Z", regNo: "013"}, {name: "Owen", dob: "1853-04-26T00:00:00.000Z", regNo: "052"}, {name: "Martin", dob: "1854-02-15T00:00:00.000Z", regNo: "063"}, {name: "Guye", dob: "1854-10-15T00:00:00.000Z", regNo: "084"}, {name: "Charles", dob: "1954-02-14T00:00:00.000Z", regNo: "091"}];


console .log (
  transform (input)
)

Это не дает список членов в стиле JSON в выводе. Я нахожу этот вывод довольно странным, но если вы хотите, просто замените

  members: group,

на

  members: JSON.stringify(group),

в constructGroup.


Вы спрашивали об использовании reduce. Хотя в этом примере его можно использовать несколько раз, ключевым из них является функция makeGroups. (Эта функция, вероятно, должна быть добавлена ​​в Ramda. Она не очень эффективна, с производительностью O (n^2) в худшем случае, но я не знаю ничего более эффективного. И она, очевидно, полезна.)

makeGroups принимает двоичный предикат и список элементов и возвращает результат их группировки, находя для каждого элемента первую группу, для которой предикат возвращает true при применении к группе и элементу, добавляя элемент в эту группу. Если ничего не найдено, запускается новая группа, содержащая только элемент. Предикат должен взять группу и элемент и сообщить, принадлежит ли элемент в группу. В текущем случае мы предоставляем предикат doesItFit, который проверяет, что длина этой группы меньше трех, а возраст элемента не превышает пяти всех элементов в группе.

0 голосов
/ 30 сентября 2019

Вот как я бы это сделал:

  1. Сначала вычислите age из dob для каждого человека.
  2. Затем используйте lower () преобразовать данные. Если группа существует, удовлетворяющая заданным критериям, то мы добавляем в нее текущего члена и пересчитываем sum, oldest и другие свойства. Если нет, мы создаем новую группу с текущим членом.

const input = [ { name: 'Hendrick', dob: '1853-07-18T00:00:00.000Z', regNo: '041', }, { name: 'Albert', dob: '1910-03-14T00:00:00.000Z', regNo: '033', }, { name: 'Marie', dob: '1953-11-07T00:00:00.000Z', regNo: '024', }, { name: 'Neils', dob: '1853-10-07T00:00:00.000Z', regNo: '02', }, { name: 'Max', dob: '1853-04-23T00:00:00.000Z', regNo: '014', }, { name: 'Erwin', dob: '1854-08-12T00:00:00.000Z', regNo: '09', }, { name: 'Auguste', dob: '1854-01-28T00:00:00.000Z', regNo: '08', }, { name: 'Karl', dob: '1852-12-05T00:00:00.000Z', regNo: '120', }, { name: 'Louis', dob: '1852-08-15T00:00:00.000Z', regNo: '022', }, { name: 'Arthur', dob: '1892-09-10T00:00:00.000Z', regNo: '321', }, { name: 'Paul', dob: '1902-08-08T00:00:00.000Z', regNo: '055', }, { name: 'William', dob: '1890-03-31T00:00:00.000Z', regNo: '013', }, { name: 'Owen', dob: '1853-04-26T00:00:00.000Z', regNo: '052', }, { name: 'Martin', dob: '1854-02-15T00:00:00.000Z', regNo: '063', }, { name: 'Guye', dob: '1854-10-15T00:00:00.000Z', regNo: '084', }, { name: 'Charles', dob: '1954-02-14T00:00:00.000Z', regNo: '091', }, ];

// Compute age from DOB.
const data = input.map(item => {
  let age = new Date().getFullYear() - new Date(item.dob).getFullYear();
  return {...item, age};
});

var result = data.reduce((acc, curr) => {
  let group = Object.values(acc).find(group => group.members && group.members.length < 3 
    && group.members.every(member => Math.abs(member.age - curr.age) < 5));

  if (group) {
    group.members.push(curr);
    group.regNos.push(curr.regNo);
    group.oldest = Math.max(...group.members.map(member => member.age));
    group.sum = group.sum + curr.age; 
  } else {
    acc.noOfGroups = acc.noOfGroups + 1 || 1;
    let groupName = "group" + acc.noOfGroups;
    acc[groupName] = {
      "members": [curr],
      "oldest": curr.age,
      "sum": curr.age,
      "regNos": [curr.regNo],
    };
  }

  return acc;
}, {});

console.log(result);
...