{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/transforming-exported-csv-data-for-use-in-a-chart",
    "result": {"data":{"site":{"siteMetadata":{"title":"your friend Joel's digital garden","description":"Articles and notes from a collaborator at egghead.io. Musings on software, business, and life from a skilled virtual assistant.","author":{"name":"Joel Hooks"},"keywords":["Video Blogger"]}},"mdx":{"excerpt":"There are many ways to build a new feature for your applications. For me, when I need a new tool, component, or functionality I like to take a step back and think about it in terms of data. What data do I have, what data do I need, and how…","fields":{"github":"https://github.com/joelhooks/joelhooks-com/tree/master/content/blog/2019-02-08-transforming-exported-csv-data-for-use-in-a-chart/index.mdx"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"slug\": \"transforming-exported-csv-data-for-use-in-a-chart\",\n  \"date\": \"2019-02-08T00:00:00.000Z\",\n  \"title\": \"Transforming exported CSV data for use in a React chart.\",\n  \"published\": true\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"There are many ways to build a new feature for your applications. For me, when I need a new tool, component, or functionality I like to take a step back and think about it in terms of data. What data do I have, what data do I need, and how am I going to present and interact with the data.\"), mdx(\"p\", null, \"At egghead, we are constantly working to improve our processes and the content that the creators we work are producing. One of the key performance indicators (KPI) we can look at to better understand how content is being consumed is dropoff rates.\"), mdx(\"p\", null, \"When somebody sits down to watch a course they start lesson one. Sometimes they \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"don't even finish the first lesson\"), \", sometimes they finish all the lessons, and then of course people stop at various points in between.\"), mdx(\"p\", null, \"In a perfect world, most folks that start will also finish. We live in reality though, and as we start to seriously consider various aspects of our core format, we need to also consider how we will understand and \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"prove\"), \" that our improvement efforts are getting real results.\"), mdx(\"p\", null, \"Otherwise you're just changing shit.\"), mdx(\"p\", null, \"We track lesson views. We do this so we can persist progress for folks using the site, as well as part of our royalty payment system for our badass content creators. While all the data that we need to calculate dropoff rates is there in the database, it isn't presented in that format anywhere in our application.\"), mdx(\"p\", null, \"We've got some options at this point:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"add api endpoints, data model changes, and UI to our existing rails app\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"export some data and put together a lightweight presentation\")), mdx(\"p\", null, \"I strongly prefer getting the data and working in a lightweight environment like codesandbox.io\"), mdx(\"p\", null, \"It's faster and more disposable so I can iterate and explore solutions quickly. There's no build to deal with. Nothing needs to load over the wire. It's easy to share and collaborate.\"), mdx(\"p\", null, \"Basically, it's great!\"), mdx(\"p\", null, \"To get data out of our Rails application I have access to a gem called Blazer. It's an incredibly handy tool. You give it some SQL, define any variables, and it performs the query. You can look at the data in the browser, or download it as a comma-separated variable (CSV) file.\"), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"1035px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/2b09eea13b6385d2d9d2ab416eb93f59/bf668/blazer-gem.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"77.60617760617761%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAACE0lEQVQ4y5VUzY6bMBjk/S99iKrv0AforYc97FZKpI0ghOCEYIyNsbFDMtX3JRCa5rJII+O/8cw3huT7rwzb7RZ5nqMoClhr4b2H7S0GNyD6yGO97bl9oIe3Fmmt8OOPwLefn/j9WSE51ApSNpBS4nQ6QSmFVre4xAtSleLt+AZ6LpfLS8RxhI0jjD8jxBGJaiSMMdBawznHp3ddh8EPkJ2E0AIxRIQQXiKGgHO8gQ5I3j8+sFqtkKYZjscjquqEzloYo2E7i6Ef0Nnuye6/4PVdhxgjktVqjTzfMZkQAoJI6xqnukYtJdeydw69629t378EETPher3GbrfjQAii2KMSAr0xsNrAaI2u1eim1jzUUmkI0zsTHg4Hrl+jbsG4poWXCkPdwCsN22oMUvFYaFoE5xHivX4x4nw+z/UcxxEJDU4PhRJixJVSBdjiEMJj3nueX66nW3G9XmckVD+y3DQ3hfuiwH6/5+Q9EQ7DvJj6pGjqu8U8JcyEZFmIA9egVS3ybQ4ao42k3vuB1Sw3TSCL09yskFRRmpRUIyU2mw1EKZiMTg93yxPBkpT6SzImrKqKrwwlRbazNEVZljMZtUvCZ4X/Ed4sC66ZvCtM03RW6Jz/GmFNF7iu2XLbtnwXy1JAG8OkpHJJ8Gz5ua5MSD8FskxXIMsyVjx9219WSBsJZI9aUki1pNQp6eWmZzXPfSL8C4wr01OQQg7HAAAAAElFTkSuQmCC')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"ui for the blazer rails gem\",\n    \"title\": \"ui for the blazer rails gem\",\n    \"src\": \"/static/2b09eea13b6385d2d9d2ab416eb93f59/e3189/blazer-gem.png\",\n    \"srcSet\": [\"/static/2b09eea13b6385d2d9d2ab416eb93f59/a2ead/blazer-gem.png 259w\", \"/static/2b09eea13b6385d2d9d2ab416eb93f59/6b9fd/blazer-gem.png 518w\", \"/static/2b09eea13b6385d2d9d2ab416eb93f59/e3189/blazer-gem.png 1035w\", \"/static/2b09eea13b6385d2d9d2ab416eb93f59/44d59/blazer-gem.png 1553w\", \"/static/2b09eea13b6385d2d9d2ab416eb93f59/a6d66/blazer-gem.png 2070w\", \"/static/2b09eea13b6385d2d9d2ab416eb93f59/bf668/blazer-gem.png 2328w\"],\n    \"sizes\": \"(max-width: 1035px) 100vw, 1035px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"I grabbed the CSV.\"), mdx(\"p\", null, \"My data has all the aspects I need. Each of the lesson videos that belongs to a \\\"series\\\" (course) is output with its position in the series, how many starts it has had, and how many completions.\"), mdx(\"p\", null, \"The format is cumbersome though, so I am going to transform it with a node script.\"), mdx(\"p\", null, \"This process is very similar to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/export-drip-tags-for-convertkit\"\n  }, \"what I did in this post\")), mdx(\"p\", null, \"Here's the full script, check it out and then I'll go through each section and explain what's happening:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const _ = require('lodash')\\nconst fs = require('fs')\\nconst csv = require('csv-parser')\\nvar reduce = require('stream-reduce')\\n\\nconst csvFilePath = './series-drop.csv'\\n\\nfs.createReadStream(csvFilePath)\\n  .pipe(csv())\\n  .pipe(\\n    reduce((acc, row) => {\\n      const series = _.find(acc, { title: row.series }) || {\\n        lessons: [],\\n        totalStarts: 0,\\n        totalCompletions: 0,\\n      }\\n      const filtered = _.filter(acc, series => series.title !== row.series)\\n\\n      return [\\n        ...filtered,\\n        {\\n          title: row.series,\\n          totalStarts: series.totalStarts + parseInt(row.starts, 10),\\n          totalCompletions:\\n            series.totalCompletions + parseInt(row.completions, 10),\\n          lessons: _.sortBy(\\n            [\\n              ...series.lessons,\\n              {\\n                title: row.lesson,\\n                starts: row.starts,\\n                completions: row.completions,\\n                position: row.position,\\n              },\\n            ],\\n            'position',\\n          ),\\n        },\\n      ]\\n    }, []),\\n  )\\n  .on('data', function(dropoffData) {\\n    fs.writeFile(\\n      'series_dropoff.json',\\n      JSON.stringify(dropoffData),\\n      'utf8',\\n      () => {\\n        console.log('done')\\n      },\\n    )\\n  })\\n\")), mdx(\"p\", null, \"Up top we have some imports.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"lodash: I love this library quite a lot and use it in every project. It provides an infinitly useful set of tools for dealing with objects and collections \\u2764\\uFE0F\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"fs: this is part of the node standard library and is used for dealing with the file system. We are going to be loading a CSV and saving a json file, so it is required.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"csv-parser: this is a stream based library for consuming CSV data and converting it to JavaScript objects for each row/line of the CSV.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"stream-reduce: this is a reduce function for streams. Because I need to transform data from one \\\"shape\\\" to another, reduce is the tool I reach for.\")), mdx(\"p\", null, \"To kick the script off the first step is to load the data:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const csvFilePath = './series-drop.csv'\\n\\nfs.createReadStream(csvFilePath)\\n\")), mdx(\"p\", null, \"We us \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"createReadStream\"), \" which streams the data from the file on disk into our application.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \".pipe(csv())\\n\")), mdx(\"p\", null, \"Streams allow us to chain together steps. In the next step we \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"pipe\"), \" the data stream into \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"csv()\"), \" which converts the binary data stream into rows. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"csv-parser\"), \" library is powerful and can do more interesting things, but luckily for us we don't need to do anything interesting so we can just call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"csv()\"), \" and have what we need.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \".pipe(\\n  reduce((acc, row) => {\\n    //we'll look at this part later...\\n  }, []),\\n\")), mdx(\"p\", null, \"We are calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"pipe\"), \" again, and this time we are piping the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"result\"), \" of the previous step that converts our file on disk into JavaScript data objects into a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"reduce\"), \" function. If you aren't familiar with reduce, or want to dig a bit deeper, I highly recommend \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/courses/reduce-data-with-javascript-array-reduce\"\n  }, \"mykola bilokonsky's awesome egghead course\"), \" on the topic!\"), mdx(\"p\", null, \"The reduce function takes two arguments.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"the reducer function. This function also takes two arguments. The \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"accumulator\"), \" and the current item. Reducers typically work on collections or objects. Things that can be iterated on. It's similar to a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"forEach\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"map\"), \", but the accumulator is what makes the reducer interesting.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"The second argument is the starting state of the accumulator. In our case we want to reduce all of the rows in our CSV file into an array of javascript objects that each represent a course so this second argument is a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"[]\"))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"reduce((acc, row) => {\\n  const series = _.find(acc, {title: row.series}) || {\\n    lessons: [],\\n    totalStarts: 0,\\n    totalCompletions: 0,\\n  }\\n  const filtered = _.filter(acc, series => series.title !== row.series)\\n\")), mdx(\"p\", null, \"Inside of our reducer we want to take the current row and add the lesson data to the series object. We use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"_.find(acc, {title: row.series})\"), \" to see if the accumulator already has an object for this series. If it doesn't we create an object to work with and assign it sensible defaults.\"), mdx(\"p\", null, \"We also create another array that filters out the existing series object so that we don't end up with doubles.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"return [\\n  ...filtered,\\n  {\\n    title: row.series,\\n    totalStarts: series.totalStarts + parseInt(row.starts, 10),\\n    totalCompletions: series.totalCompletions + parseInt(row.completions, 10),\\n    lessons: _.sortBy(\\n      [\\n        ...series.lessons,\\n        {\\n          title: row.lesson,\\n          starts: row.starts,\\n          completions: row.completions,\\n          position: row.position,\\n        },\\n      ],\\n      'position',\\n    ),\\n  },\\n]\\n\")), mdx(\"p\", null, \"Our reducer function returns \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"a new array\"), \" that becomes the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"acc\"), \" accumulator for the next row.\"), mdx(\"p\", null, \"We spread the filtered array \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"...filtered\"), \" so that all of the items in that array are in our new array. Then we add a new object that represents the current row series into the array.\"), mdx(\"p\", null, \"Notice we didn't \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"mutate\"), \" any data. We built a new array to return as our new accumulator.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"{\\n  title: row.series,\\n  totalStarts: series.totalStarts + parseInt(row.starts, 10),\\n  totalCompletions:\\n    series.totalCompletions + parseInt(row.completions, 10),\\n  lessons: _.sortBy(\\n    [\\n      ...series.lessons,\\n      {\\n        title: row.lesson,\\n        starts: row.starts,\\n        completions: row.completions,\\n        position: row.position,\\n      },\\n    ],\\n    'position',\\n  ),\\n}\\n\")), mdx(\"p\", null, \"This is the new (or updated) series object. It sets the title, adds the current row's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"starts\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"completions\"), \" to the previous count (we set it to 0 if it's a newly accumulated series), and finally we add the lessons.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"lessons: _.sortBy(\\n  [\\n    ...series.lessons,\\n    {\\n      title: row.lesson,\\n      starts: row.starts,\\n      completions: row.completions,\\n      position: row.position,\\n    },\\n  ],\\n  'position',\\n)\\n\")), mdx(\"p\", null, \"The lessons get added with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sortBy\"), \" from lodash, which sorts an array based on the second argument. In this case we just pass the string \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'position'\"), \" which tells lodash to sort on the position property of the objects in the array.\"), mdx(\"p\", null, \"Inside of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sortBy\"), \" we use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"...series.lessons\"), \" to spread any existing lessons in the accumulated series back into the new series object. Then we can add the data from the current row as a new lesson into the accumulated series object.\"), mdx(\"p\", null, \"This part gets a little weird if you aren't used to reducers and (re)building objects using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"...\"), \" spread operator, but it's worth practicing and thinking about because it is a very useful tool. If you'd like a really great explaination of this, I highly recommend \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://egghead.io/lessons/react-redux-avoiding-array-mutations-with-concat-slice-and-spread\"\n  }, \"this egghead video from Dan Abramov\"), \".\"), mdx(\"p\", null, \"To get a clearer picture of what just happened, we have reduced/transformed this csv data:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-csv\"\n  }, \"add-internationalization-i18n-to-a-react-app-using-react-intl,react-install-and-configure-the-entry-point-of-react-intl,-5,530,428\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-convert-a-hard-coded-string-using-react-intl-formattedmessage,4194299,498,370\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-render-content-with-placeholders-using-react-intl-formattedmessage,6291451,305,233\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-render-content-with-markup-using-react-intl-formattedhtmlmessage,7340027,259,234\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-format-date-and-time-using-react-intl-formatteddate-and-formattedtime,7864315,244,210\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-format-a-date-relative-to-the-current-date-using-react-intl-formattedrelative,8388602,201,192\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-format-numbers-with-separators-and-currency-symbols-using-react-intl-formattednumber,8388603,216,197\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-render-content-based-on-a-number-using-react-intl-formattedmessage,8388604,203,174\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-use-a-react-intl-higher-order-component-to-format-messages-and-get-current-locale,8388605,251,199\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-write-tests-for-react-intl-output-using-enzyme-and-jest,8388606,242,144\\nadd-internationalization-i18n-to-a-react-app-using-react-intl,react-use-webpack-to-conditionally-include-an-intl-polyfill-for-older-browsers,8388607,187,154\\n\")), mdx(\"p\", null, \"Into a nicely structured JS object like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"[{\\n  \\\"title\\\": \\\"add-internationalization-i18n-to-a-react-app-using-react-intl\\\",\\n  \\\"totalStarts\\\": 3136,\\n  \\\"totalCompletions\\\": 2535,\\n  \\\"lessons\\\": [\\n    {\\n      \\\"title\\\": \\\"react-install-and-configure-the-entry-point-of-react-intl\\\",\\n      \\\"starts\\\": \\\"530\\\",\\n      \\\"completions\\\": \\\"428\\\",\\n      \\\"position\\\": \\\"-5\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-convert-a-hard-coded-string-using-react-intl-formattedmessage\\\",\\n      \\\"starts\\\": \\\"498\\\",\\n      \\\"completions\\\": \\\"370\\\",\\n      \\\"position\\\": \\\"4194299\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-render-content-with-placeholders-using-react-intl-formattedmessage\\\",\\n      \\\"starts\\\": \\\"305\\\",\\n      \\\"completions\\\": \\\"233\\\",\\n      \\\"position\\\": \\\"6291451\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-render-content-with-markup-using-react-intl-formattedhtmlmessage\\\",\\n      \\\"starts\\\": \\\"259\\\",\\n      \\\"completions\\\": \\\"234\\\",\\n      \\\"position\\\": \\\"7340027\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-format-date-and-time-using-react-intl-formatteddate-and-formattedtime\\\",\\n      \\\"starts\\\": \\\"244\\\",\\n      \\\"completions\\\": \\\"210\\\",\\n      \\\"position\\\": \\\"7864315\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-format-a-date-relative-to-the-current-date-using-react-intl-formattedrelative\\\",\\n      \\\"starts\\\": \\\"201\\\",\\n      \\\"completions\\\": \\\"192\\\",\\n      \\\"position\\\": \\\"8388602\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-format-numbers-with-separators-and-currency-symbols-using-react-intl-formattednumber\\\",\\n      \\\"starts\\\": \\\"216\\\",\\n      \\\"completions\\\": \\\"197\\\",\\n      \\\"position\\\": \\\"8388603\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-render-content-based-on-a-number-using-react-intl-formattedmessage\\\",\\n      \\\"starts\\\": \\\"203\\\",\\n      \\\"completions\\\": \\\"174\\\",\\n      \\\"position\\\": \\\"8388604\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-use-a-react-intl-higher-order-component-to-format-messages-and-get-current-locale\\\",\\n      \\\"starts\\\": \\\"251\\\",\\n      \\\"completions\\\": \\\"199\\\",\\n      \\\"position\\\": \\\"8388605\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-write-tests-for-react-intl-output-using-enzyme-and-jest\\\",\\n      \\\"starts\\\": \\\"242\\\",\\n      \\\"completions\\\": \\\"144\\\",\\n      \\\"position\\\": \\\"8388606\\\"\\n    },\\n    {\\n      \\\"title\\\": \\\"react-use-webpack-to-conditionally-include-an-intl-polyfill-for-older-browsers\\\",\\n      \\\"starts\\\": \\\"187\\\",\\n      \\\"completions\\\": \\\"154\\\",\\n      \\\"position\\\": \\\"8388607\\\"\\n    }\\n  ]\\n},\\n...\\n]\\n\")), mdx(\"p\", null, \"All that's left is to write it out to a file so we can use it elsewhere:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \".on('data', function(dropoffData) {\\n  fs.writeFile(\\n    'series_dropoff.json',\\n    JSON.stringify(dropoffData),\\n    'utf8',\\n    () => {\\n      console.log('done')\\n    },\\n  )\\n})\\n\")), mdx(\"p\", null, \"Streams can listen for standard events. In this case we are listening for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"data\"), \" which is ommitted by \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"stream-reducer\"), \" when it is done. We can then use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs.writeFile\"), \" to write out our reduced array of series objects to disk!\"), mdx(\"p\", null, \"Armed with well formated data it was a lot easier to open up codesandbox and start playing with it. I ended up choosing react-charts and a bargraph.\"), mdx(\"iframe\", {\n    src: \"https://codesandbox.io/embed/6jj1kr8lxk?autoresize=1&hidenavigation=1&module=%2Fsrc%2FChart.js&view=preview\",\n    style: {\n      \"width\": \"100%\",\n      \"height\": \"500px\",\n      \"border\": \"0\",\n      \"borderRadius\": \"4px\",\n      \"overflow\": \"hidden\"\n    },\n    sandbox: \"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\"\n  }), mdx(\"p\", null, \"Feel free to explore the codesandbox to see how the data gets used to create a chart.\"), mdx(\"p\", null, \"If you'd like to watch me stumble around and figure this all out in realtime, today's your lucky day. I recorded it all and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.youtube.com/playlist?list=PL1g0o_QdVLU_PPf_tf5QPMJpXLcn_Xy5j\"\n  }, \"posted it to this playlist\"), \". \\uD83D\\uDE42\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Transforming exported CSV data for use in a React chart.","date":"February 08, 2019","banner":null,"slug":"transforming-exported-csv-data-for-use-in-a-chart","keywords":null}}},"pageContext":{"id":"674a3869-52d8-509f-9fbf-9111b665ca58","prev":{"id":"423a41b5-7d73-51bb-8040-40e9cf94ef60","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"What is a badass? I love the word itself, because there is practically no way to use it in a negative way. It's a  good  word. In Kathy Sierra's book, the word  badass  is used to describe an expert. Somebody that has learned a skill, crossed the…","fields":{"title":"Badass: Making Users Awesome by Kathy Sierra","slug":"badass-making-users-awesome-by-kathy-sierra","date":"2019-02-24T00:00:00.000Z"}},"next":{"id":"b45e0e76-70dc-5eda-97a1-b861f1ff3427","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"This blog is built with Gatsby and uses MDX for the post content. It's a great setup, and so far I've enjoyed using it very much. One thing I missed from my old Octopress/Jekyll configuration was the ability to run a rake task to create a new post…","fields":{"title":"A handy npm script for creating a new Gatsby blog post","slug":"a-handy-npm-script-for-creating-a-new-gatsby-blog-post","date":"2019-01-30T00:00:00.000Z"}}}},
    "staticQueryHashes": ["1045846374"]}