【问题标题】:Using 'reduce' to manipulate an object and produce an array使用“reduce”操作对象并生成数组
【发布时间】:2020-01-29 20:20:35
【问题描述】:

我知道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;

输入

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;

输出

{ 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 ] } }

【问题讨论】:

标签: javascript typescript ecmascript-6 mapreduce reduce


【解决方案1】:

我会这样做:

  1. 首先从dob 为每个人计算age
  2. 然后使用reduce() 转换数据。如果存在满足给定条件的组,则我们将当前成员添加到该组并重新计算sumoldest 和其他属性。如果没有,我们会使用当前成员创建一个新组。

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);

【讨论】:

  • 感谢 Nikhil 这很有帮助
【解决方案2】:

我首先使用Ramda 写了这篇文章(免责声明:我是它的作者之一。)这是代码:

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

使用附加的辅助函数addAgemakeGroupsdoesItFitnameGroupsconstructObj)。我真的很喜欢这种逐步转换输出的方式。

但 Ramda 只是帮助函数的集合(设计为允许某种特定的编码风格,可以肯定),并且我们自己编写这些版本很容易。

但是把这样的功能放在

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

到我们自己的实用程序库中,我们可以相对容易地构建像上面一样干净的代码。

对于这种情况,我最终使用了许多 Ramda 函数,以及可能属于此类库的两个助手(numericSortmakeGroups。)但是这些对于我们自己来说很容易编写,而且我们可以在它们之上构建此功能。

// 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,它检查该组的长度是否小于 3,并且该项目的年龄是否在该组中所有项目的 5 以内。

【讨论】:

  • 非常感谢。下次我们会尝试使用 Ramda
猜你喜欢
  • 2021-12-02
  • 1970-01-01
  • 2021-03-02
  • 2021-10-10
  • 2020-01-07
  • 1970-01-01
  • 2018-12-26
  • 1970-01-01
  • 2022-10-13
相关资源
最近更新 更多