Лучше использовать преобразование со списком объектов, вместо использования 2 редукторов - PullRequest
2 голосов
/ 25 февраля 2020

Рассмотрим следующий список объектов:

const blogs = [
    {
      title: "React patterns",
      author: "Michael Chan",
      url: "https://reactpatterns.com/",
      likes: 7,
    },
    {
      title: "Go To Statement Considered Harmful",
      author: "Edsger W. Dijkstra",
      likes: 5,
    },
    {
      title: "Canonical string reduction",
      author: "Edsger W. Dijkstra",
      likes: 12,
    },
    {
      title: "First class tests",
      author: "Robert C. Martin",
      likes: 10,
    },
    {
      title: "TDD harms architecture",
      author: "Robert C. Martin",
      likes: 0,
    },
    {
      title: "Type wars",
      author: "Robert C. Martin",
      likes: 2,
    }  
]

Что мне нужно сделать, это написать функцию, которая возвращает объект блога, с автором, у которого есть большинство блогов, которые он написал, в следующем формат (который также является ожидаемым результатом в этом случае):

{
    author: "Robert C. Martin",
    blogs: 3
}

Это первое решение, которое я придумала, включающее 2 редуктора

const mostBlogs = blogs => {
    const formatted_blogs = blogs.reduce((acc, cur_blog)=>{
        if (!acc[cur_blog.author]){
            acc[cur_blog.author] = {
                author: cur_blog.author, blogs: 1
            }
        } else {
            acc[cur_blog.author].blogs++;
        }
        return acc;
    }, {});

    return Object.keys(formatted_blogs).reduce((acc, cur)=>{
        if (formatted_blogs[cur].blogs > (acc.blogs||0))
            return ( {...formatted_blogs[cur]} )
        return acc;
    }, {});
}

Мне было интересно, как я могу сделать это лучше, используя только один редуктор, который был бы более эффективным и умным. Пока я набирал вопрос здесь, я фактически нашел другое решение, использующее только 1 редуктор. Хотелось бы услышать ваше мнение об этом, можно ли это оптимизировать? Это достаточно умный / эффективный? или я могу сделать что-то другое, что было бы лучше?

const mostBlogs = blogs => {
    const { most_blogs } = blogs.reduce((acc, cur_blog)=>{
        // if author doesn't exist in acc.all, add a new formatted obj
        if (!acc.all[cur_blog.author])
            acc.all[cur_blog.author] = { author: cur_blog.author, blogs: 1 };
        // if exists, simply increment the blogs prop count in acc.all
        else 
            acc.all[cur_blog.author].blogs++;

        // check if cur_blog blogs prop in acc.all is higher than
        // existing one in acc.most_blogs blogs prop, overwrite obj if so
        if (acc.all[cur_blog.author].blogs > (acc.most_blogs.blogs||0))
            acc.most_blogs = acc.all[cur_blog.author];

        return acc;
    }, {all: {}, most_blogs: {} });

    return most_blogs;
}

Ответы [ 5 ]

2 голосов
/ 25 февраля 2020

Вы можете взять один l oop с массивом для верхних значений.

const
    blogs = [{ title: "React patterns", author: "Michael Chan", url: "https://reactpatterns.com/", likes: 7 }, { title: "Go To Statement Considered Harmful", author: "Edsger W. Dijkstra", likes: 5 }, { title: "Canonical string reduction", author: "Edsger W. Dijkstra", likes: 12 }, { title: "First class tests", author: "Robert C. Martin", likes: 10 }, { title: "TDD harms architecture", author: "Robert C. Martin", likes: 0 }, { title: "Type wars", author: "Robert C. Martin", likes: 2 }],
    mostBlogs = blogs => blogs
        .reduce((acc, { author })=> {
            var top = acc.top.length && acc.top[0].blogs;
            if (!acc.authors[author]) acc.authors[author] = { author, blogs: 0 };
            if (top < ++acc.authors[author].blogs) acc.top = [acc.authors[author]];
            if (top === acc.authors[author].blogs) acc.top.push(acc.authors[author]);
            return acc;
        }, { authors: {}, top: [] })
        .top;

console.log(mostBlogs(blogs));
1 голос
/ 26 февраля 2020

Я бы так и сделал.

const blogs = [{"title":"React patterns","author":"Michael Chan","url":"https://reactpatterns.com/","likes":7},{"title":"Go To Statement Considered Harmful","author":"Edsger W. Dijkstra","likes":5},{"title":"Canonical string reduction","author":"Edsger W. Dijkstra","likes":12},{"title":"First class tests","author":"Robert C. Martin","likes":10},{"title":"TDD harms architecture","author":"Robert C. Martin","likes":0},{"title":"Type wars","author":"Robert C. Martin","likes":2}];

const reducer = ({ mode, histogram }, { author }) => {
    const { [author]: frequency = 0 } = histogram;
    histogram[author] = frequency + 1;
    return { mode: histogram[author] > histogram[mode] ? author : mode, histogram };
};

const mostBlogs = blogs => {
    const initial = { mode: blogs[0].author, histogram: {} };
    const { mode, histogram: { [mode]: frequency } } = blogs.reduce(reducer, initial);
    return { author: mode, blogs: frequency };
};

console.log(mostBlogs(blogs));
0 голосов
/ 25 февраля 2020

Вот простое решение с использованием ванили JS.

const input = [{
    title: "React patterns",
    author: "Michael Chan",
    url: "https://reactpatterns.com/",
    likes: 7,
  },
  {
    title: "Go To Statement Considered Harmful",
    author: "Edsger W. Dijkstra",
    likes: 5,
  },
  {
    title: "Canonical string reduction",
    author: "Edsger W. Dijkstra",
    likes: 12,
  },
  {
    title: "First class tests",
    author: "Robert C. Martin",
    likes: 10,
  },
  {
    title: "TDD harms architecture",
    author: "Robert C. Martin",
    likes: 0,
  },
  {
    title: "Type wars",
    author: "Robert C. Martin",
    likes: 2,
  }
]

const topBloggers = input.reduce ((o, { author }) => 
   ({ ...o, [author]: o[author] ? o[author] + 1 : 1 }), {})

const [[author, blogs]] = Object.entries (topBloggers)
                         .sort (([,countA], [,countB]) => countB - countA)

const topBlogger = { author, blogs }

console.log (topBlogger)
0 голосов
/ 25 февраля 2020

Если вы готовы взять дополнительную библиотеку размером 8 КБ (отказ от ответственности ... она моя), вы можете использовать blinq. Это делает такие вещи легкими и разборчивыми:

import { blinq } from "blinq";
//...
const v = blinq(blogs)
  .groupBy(blog => blog.author)
  .select(grp => ({ author: grp.key, blogs: grp.count() }))
  .maxBy(o => o.blogs) //returns a sequence... there might be several maximums
  .first();

const {
  blinq
} = window.blinq
const blogs = [{
    title: "React patterns",
    author: "Michael Chan",
    url: "https://reactpatterns.com/",
    likes: 7
  },
  {
    title: "Go To Statement Considered Harmful",
    author: "Edsger W. Dijkstra",
    likes: 5
  },
  {
    title: "Canonical string reduction",
    author: "Edsger W. Dijkstra",
    likes: 12
  },
  {
    title: "First class tests",
    author: "Robert C. Martin",
    likes: 10
  },
  {
    title: "TDD harms architecture",
    author: "Robert C. Martin",
    likes: 0
  },
  {
    title: "Type wars",
    author: "Robert C. Martin",
    likes: 2
  }
];

const v = blinq(blogs)
  .groupBy(blog => blog.author)
  .select(grp => ({
    author: grp.key,
    blogs: grp.count()
  }))
  .maxBy(o => o.blogs) //returns a sequence... there might be several maximums
  .first();

console.log(v);
<script src="https://cdn.jsdelivr.net/npm/blinq"></script>
0 голосов
/ 25 февраля 2020

Можно использовать reduce метод для подсчета title, а затем просто sort их по названиям:

const grouped = Object.values(blogs.reduce((a, {author, title}) => {
    a[author] = a[author] || {author, blogs: 0};
    a[author].blogs += 1;
    return a;
}, {}));


grouped.sort((a, b) => b.blogs - a.blogs)[0];

Я был вдохновлен ответ Нины Шольц ( Спасибо за показ такого замечательного способа использования аккумулятора в методе Reduce). На мой взгляд, ответ Нины - лучший и должен быть отмечен как ответ. Возможно, это немного измененная версия ответа Нины, однако, на мой взгляд, ответ должен быть опубликован. Таким образом, эта версия имеет O(1) сложность:

const grouped = blogs.reduce((a, { author }) => {
    a[author] = a[author] || {author, blogs: 0};
    a[author].blogs += 1;
    if (a.topAuthor.hasOwnProperty('blogs') && a[author].blogs >  a.topAuthor.blogs)
        a.topAuthor = a[author];
    else if (Object.keys(a.topAuthor) == 0)
        a.topAuthor = a[author];
    return a;
}, {author: {}, topAuthor: {}});

console.log(grouped.topAuthor);

Пример:

const blogs = [
    {
      title: "React patterns", author: "Michael Chan",
      url: "https://reactpatterns.com/", likes: 7,
    },
    {
      title: "Go To Statement Considered Harmful", author: "Edsger W. Dijkstra", 
      likes: 5,
    },
    {
      title: "Canonical string reduction", author: "Edsger W. Dijkstra",
      likes: 12,
    },
    {
      title: "First class tests", author: "Robert C. Martin",
      likes: 10,
    },
    {
      title: "TDD harms architecture", author: "Robert C. Martin", likes: 0,
    },
    {
      title: "Type wars", author: "Robert C. Martin", likes: 2,
    }
];

const grouped = blogs.reduce((a, { author }) => {
    a[author] = a[author] || {author, blogs: 0};
    a[author].blogs += 1;
    if (a.topAuthor.hasOwnProperty('blogs') && a[author].blogs >  a.topAuthor.blogs)
        a.topAuthor = a[author];
    else if (Object.keys(a.topAuthor) == 0)
        a.topAuthor = a[author];
    return a;
}, {author: {}, topAuthor: {}});

console.log(grouped.topAuthor);
...