1

I need to create a Graphql query that outputs data from two arrays of objects. The arrays are:

  const authors = [
    {
      name: 'Robert Martin',
      id: 'afa51ab0-344d-11e9-a414-719c6709cf3e',
      born: 1952
    },
    {
      name: 'Martin Fowler',
      id: 'afa5b6f0-344d-11e9-a414-719c6709cf3e',
      born: 1963
    },
    {
      name: 'Fyodor Dostoevsky',
      id: 'afa5b6f1-344d-11e9-a414-719c6709cf3e',
      born: 1821
    },
    {
      name: 'Joshua Kerievsky', // birthyear not known
      id: 'afa5b6f2-344d-11e9-a414-719c6709cf3e'
    },
    {
      name: 'Sandi Metz', // birthyear not known
      id: 'afa5b6f3-344d-11e9-a414-719c6709cf3e'
    }
  ];

And:

  const books = [
    {
      title: 'Clean Code',
      published: 2008,
      author: 'Robert Martin',
      id: 'afa5b6f4-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring']
    },
    {
      title: 'Agile software development',
      published: 2002,
      author: 'Robert Martin',
      id: 'afa5b6f5-344d-11e9-a414-719c6709cf3e',
      genres: ['agile', 'patterns', 'design']
    },
    {
      title: 'Refactoring, edition 2',
      published: 2018,
      author: 'Martin Fowler',
      id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring']
    },
    {
      title: 'Refactoring, edition 3',
      published: 2018,
      author: 'Martin Fowler',
      id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring']
    },
    {
      title: 'Refactoring, edition 4',
      published: 2018,
      author: 'Martin Cowler',
      id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring']
    },
    {
      title: 'Refactoring to patterns',
      published: 2008,
      author: 'Joshua Kerievsky',
      id: 'afa5de01-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring', 'patterns']
    },
    {
      title: 'Practical Object-Oriented Design, An Agile Primer Using 
       Ruby',
      published: 2012,
      author: 'Sandi Metz',
      id: 'afa5de02-344d-11e9-a414-719c6709cf3e',
      genres: ['refactoring', 'design']
    },
    {
      title: 'Crime and punishment',
      published: 1866,
      author: 'Fyodor Dostoevsky',
      id: 'afa5de03-344d-11e9-a414-719c6709cf3e',
      genres: ['classic', 'crime']
    },
    {
      title: 'The Demon ',
      published: 1872,
      author: 'Fyodor Dostoevsky',
      id: 'afa5de04-344d-11e9-a414-719c6709cf3e',
      genres: ['classic', 'revolution']
    }
  ];

The desired output format for a query like this:

query {
  allAuthors {
  name
  bookCount
 }

}

is like so:

    "data": {
      "allAuthors": [
        {
          "name": "Robert Martin",
          "bookCount": 2
        },
        {
          "name": "Martin Fowler",
          "bookCount": 1
        },
        {
          "name": "Fyodor Dostoevsky",
          "bookCount": 2
        },
        {
          "name": "Joshua Kerievsky",
          "bookCount": 1
        },
        {
          "name": "Sandi Metz",
          "bookCount": 1
        }
      ]
    }

I've found a way to count the amount of books for each author and output the data in the desired format (a good example of that here: Summarize count of occurrences in an array of objects with Array#reduce). However this approach ignores other fields in the data, such as "born" and "genres". If I was to expand the query like so:

    query {
     allAuthors {
      name
      bookCount
      born
     }
   }

It wouldn't output anything for the field "born". What would be the smart way to create the query resolver? Spread operator? Reduce?

* EDIT * My unnecessarily complicated solution for counting the books here:

 const newBooks = books.reduce((acc, cv) => {
    const arr = acc.filter(obj => {
      return obj.author === cv.author;
    });

    if (arr.length === 0) {
      acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
    } else {
      arr[0].bookCount += 1;
    }

    return acc;
  }, []);
  const array = [];
  books.forEach(book => {
    const object = {
      name: book.author
    };
    array.push(object);
    return array;
  });
  const unique = array.map(a => a.name);
  result = {};
  for (var i = 0; i < unique.length; ++i) {
    if (!result[unique[i]]) result[unique[i]] = 0;
    ++result[unique[i]];
  }
  const entries = Object.entries(result);
  const finalAnswer = [];
  entries.forEach(entry => {
    const object = {
      name: entry[0],
      bookCount: entry[1]
    };
    finalAnswer.push(object);
    return finalAnswer;
  });
  console.log(finalAnswer);
1
  • What code counts the books? Commented May 22, 2019 at 6:41

2 Answers 2

3

You could map the authors and use filter to get the bookCount for each author

const authors=[{name:'Robert Martin',id:'afa51ab0-344d-11e9-a414-719c6709cf3e',born:1952},{name:'Martin Fowler',id:'afa5b6f0-344d-11e9-a414-719c6709cf3e',born:1963},{name:'Fyodor Dostoevsky',id:'afa5b6f1-344d-11e9-a414-719c6709cf3e',born:1821},{name:'Joshua Kerievsky',id:'afa5b6f2-344d-11e9-a414-719c6709cf3e'},{name:'Sandi Metz',id:'afa5b6f3-344d-11e9-a414-719c6709cf3e'}],
      books=[{title:'Clean Code',published:2008,author:'Robert Martin',id:'afa5b6f4-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Agile software development',published:2002,author:'Robert Martin',id:'afa5b6f5-344d-11e9-a414-719c6709cf3e',genres:['agile','patterns','design']},{title:'Refactoring, edition 2',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 3',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 4',published:2018,author:'Martin Cowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring to patterns',published:2008,author:'Joshua Kerievsky',id:'afa5de01-344d-11e9-a414-719c6709cf3e',genres:['refactoring','patterns']},{title:'Practical Object-Oriented Design, An Agile Primer Using Ruby ',published:2012,author:'Sandi Metz',id:'afa5de02-344d-11e9-a414-719c6709cf3e',genres:['refactoring','design']},{title:'Crime and punishment',published:1866,author:'Fyodor Dostoevsky',id:'afa5de03-344d-11e9-a414-719c6709cf3e',genres:['classic','crime']},{title:'The Demon ',published:1872,author:'Fyodor Dostoevsky',id:'afa5de04-344d-11e9-a414-719c6709cf3e',genres:['classic','revolution']}];

const output = authors.map(({ born, name }) => {
  const bookCount = books.filter(b => b.author === name).length;
  return { name, born, bookCount }
})

console.log(output)

Sign up to request clarification or add additional context in comments.

Comments

2

I think you can add a statement to your reducer function to add the desired fields. I added the single line, and annotated the rest of the method so you can see what's going on:

const newBooks = books.reduce((acc, cv) => {
    // acc is an "accumulation" of the results so far.
    // cv is the next item that hasn't been processed.

    // Search for author in "accumulator" array acc. Put results in arr.
    const arr = acc.filter(obj => {
      return obj.author === cv.author;
    });

    if (arr.length === 0) {
      // Haven't seen this author, yet. Add new item to "accumulator" array.
      acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
    } else {
      // This author already exists in "accumulator" array, referenced by arr[0].
      // Update pre-existing item.
      arr[0].bookCount += 1;
      arr[0].born = cv.born;  // <-- This is the new code that is required.
    }

    return acc;
}, []);

2 Comments

This works for including another field from books-array. However the field "born" is in the authors-array.
@tekos: Oh, in that case I would create an authorsByName object from the authors array. Then I could look up author details given a name like this: authorsByName['Robert Martin'].born. Let me know if need an explanation of how to do this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.