Использование lodash.groupBy () для нормализации объектов - PullRequest
0 голосов
/ 24 ноября 2018

У меня есть денормализованный массив из трех разных объектов - country, school, student

const input= 
 [
    {
      countryID: 12,
      country: "US",
      schoolID: 122,
      schoolZone: "Highlands",
      studentID: 142,
      studentName: "Mary"
    },
    {
      countryID: 12,
      country: "US",
      schoolID: 122,
      schoolZone: "Highlands",
      studentID: 145,
      studentName: "John"
    },
    {
      countryID: 14,
      country: "UK",
      schoolID: 222,
      schoolZone: "Leeds",
      studentID: 223,
      studentName: "Peter"
    }
  ];

Я хотел бы нормализовать их следующим образом

const result= 
 [
    {
      countryID: 12,
      country: "US",
      schools: [
        {
          schoolID: 122,
          schoolZone: "Highlands",
          students: [
            {
              studentID: 142,
              studentName: "Mary"
            },
            {
              studentID: 145,
              studentName: "John"
            }
          ]
        }
      ]
    },
    {
      countryID: 14,
      country: "UK",
      schools: [
        {
          schoolID: 222,
          schoolZone: "Leeds",
          students: [
            {
              studentID: 223,
              studentName: "Peter"
            }
          ]
        }
      ]
    }
  ];

Я понимаю lodash и ramda можно использовать для достижения вложенной нормализации

Но я новичок в обеих библиотеках.

Не могли бы вы помочь мне с реализацией?Также интересно, если карри будет рассмотрением, учитывая, что есть вложенность schools и students

Ответы [ 3 ]

0 голосов
/ 24 ноября 2018

Это может быть способ без использования lodash или ramda.Надеюсь, это поможет.

const input= [{
    countryID: 12,
    country: "US",
    schoolID: 122,
    schoolZone: "Highlands",
    studentID: 142,
    studentName: "Mary"
}, {
    countryID: 12,
    country: "US",
    schoolID: 122,
    schoolZone: "Highlands",
    studentID: 145,
    studentName: "John"
}, {
    countryID: 14,
    country: "UK",
    schoolID: 222,
    schoolZone: "Leeds",
    studentID: 223,
    studentName: "Peter"
}];

const normalize = (data) =>

    data.reduce((acc, { studentID, studentName, schoolID, schoolZone, countryID, country }) => {

        const studentToAdd = { studentID, studentName };

        const schoolToAdd = { schoolID, schoolZone, students: [studentToAdd] };

        const countryToAdd = { countryID, country, schools: [schoolToAdd] };

        const existingCountry = acc.find(item => item.countryID === countryID);

        if (existingCountry) {

            const existingSchool = existingCountry.schools.find(item => item.schoolID === schoolID);

            if (existingSchool) {

                const existingStudent = existingSchool.students.find(item => item.studentID === studentID);

                if (!existingStudent) existingSchool.students.push(studentToAdd);
            }

            else existingCountry.schools.push(schoolToAdd);
        }

        else acc.push(countryToAdd);

        return acc;
    }, []);

console.log(normalize(input));
0 голосов
/ 25 ноября 2018

С помощью lodash вы можете решить эту проблему следующим кратким образом:

const data = [{ countryID: 12, country: "US", schoolID: 122, schoolZone: "Highlands", studentID: 142, studentName: "Mary" }, { countryID: 12, country: "US", schoolID: 122, schoolZone: "Highlands", studentID: 145, studentName: "John" }, { countryID: 14, country: "UK", schoolID: 222, schoolZone: "Leeds", studentID: 223, studentName: "Peter" } ];

const grp = (arr, key, field, group, o=0) => _(arr)
   .groupBy(key)
   .mapValues(x => ({ 
     [key]: _.head(x)[key], 
     [field]: _.head(x)[field],
     [group]: o 
       ? _.map(x, s => _.pick(s, ['studentID', 'studentName'])) 
       : grp(x, 'schoolID', 'schoolZone', 'students', 1)
     }))
    .values()
   .value()
console.log(grp(data, 'countryID', 'country', 'schools'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Идея состоит в том, чтобы использовать рекурсивный подход, когда вы вызываете одну и ту же функцию группировки 2 раза.Внутри которого мы бы _.groupBy и просто сделали бы _.mapValues

Теперь точно такая же идея с компактным кодом ES6 будет выглядеть так:

const data = [{ countryID: 12, country: "US", schoolID: 122, schoolZone: "Highlands", studentID: 142, studentName: "Mary" }, { countryID: 12, country: "US", schoolID: 122, schoolZone: "Highlands", studentID: 145, studentName: "John" }, { countryID: 14, country: "UK", schoolID: 222, schoolZone: "Leeds", studentID: 223, studentName: "Peter" } ];

const groupBy = (arr, k) => arr.reduce((r,c) => (r[c[k]] = [...r[c[k]] || [], c], r), {})
const pick = (obj, fields) => fields.reduce((r,f) => (r[f] = obj[f], r), {})

const grp = (arr, k, f, b, o=0) => 
   Object.values(Object.values(groupBy(arr, k)).map(x => ({ 
     [k]: x[0][k], 
     [f]: x[0][f],
     [b]: o 
      ? x.map(s => pick(s, ['studentID', 'studentName'])) 
      : grp(x, 'schoolID', 'schoolZone', 'students', 1)
   })))

console.log(grp(data, 'countryID', 'country', 'schools'))
0 голосов
/ 24 ноября 2018

Сначала измените каждый объект, чтобы он выглядел как окончательная структура данных:

reshape({
  countryID: 12,
  country: "US",
  schoolID: 122,
  schoolZone: "Highlands",
  studentID: 142,
  studentName: "Mary" });

//=> {
//=>     country: "US",
//=>     countryID: 12,
//=>     schools: [
//=>         {
//=>             schoolID: 122,
//=>             schoolZone: "Highlands",
//=>             students: [
//=>                 {
//=>                     studentID: 142,
//=>                     studentName: "Mary"
//=>                 }
//=>             ]
//=>         }
//=>     ]
//=> }

Затем нужно просто группировать по странам, а затем по школам и использовать функцию карты и сокращения для объединения объектов.

const {pick, map, groupBy, values, lensProp, over, reduce, mergeWithKey, pipe, prop, applySpec, converge, set, of} = R;

const input = [
  { countryID: 12,
    country: "US",
    schoolID: 122,
    schoolZone: "Highlands",
    studentID: 142,
    studentName: "Mary" },
  
  { countryID: 12,
    country: "US",
    schoolID: 122,
    schoolZone: "Highlands",
    studentID: 145,
    studentName: "John" },
  
  { countryID: 14,
    country: "UK",
    schoolID: 222,
    schoolZone: "Leeds",
    studentID: 223,
    studentName: "Peter" }];

const reshape = applySpec({
  countryID: prop('countryID'),
  country: prop('country'),
  schools: converge(
    pipe(set(lensProp('students')), of), [
      pipe(pick(['studentID', 'studentName']), of),
      pick(['schoolID', 'schoolZone'])])
});

const mergeArrays = key => mergeWithKey((k, a, b) => k === key ? [a[0], b[0]] : a);

const groupStudents = pipe(
  groupBy(prop('schoolID')),
  values,
  map(reduce(mergeArrays('students'), {})));

const process = pipe(
  map(reshape),
  groupBy(prop('countryID')),
  map(reduce(mergeArrays('schools'), {})),
  map(over(lensProp('schools'), groupStudents)),
  values);

console.log(process(input));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
...