<commit_before>// var data = [
//   ['name', 'borough', 'age', 'gender'],
//   ['patrick', 'brooklyn', '28', 'm'],
//   ['greg', 'brooklyn', '29', 'm'],
//   ['niles', 'manhattan', '30', 'm'],
//   ['jared', 'manhattan', '29', 'm'],
//   ['markus', 'manhattan', '28', 'm'],
//   ['sarah', 'queens', '30', 'f'],
//   ['vishakh', 'queens', '28', 'm'],
//   ['jessica', 'brooklyn', '28', 'f']
// ];

var data = [
  {name: 'patrick', borough: 'brooklyn', age: '28', gender: 'm'},
  {name: 'greg', borough: 'brooklyn', age: '29', gender: 'm'},
  {name: 'niles', borough: 'manhattan', age: '30', gender: 'm'},
  {name: 'jared', borough: 'manhattan', age: '29', gender: 'm'},
  {name: 'markus', borough: 'manhattan', age: '28', gender: 'm'},
  {name: 'sarah', borough: 'queens', age: '30', gender: 'f'},
  {name: 'vishakh', borough: 'queens', age: '28', gender: 'm'},
  {name: 'jessica', borough: 'brooklyn', age: '28', gender: 'f'}
];

function groupByCategory(data, groupBy){
  return data.reduce((acc, curr) =>{
    var category = curr[groupBy]
    if(!acc[category]) acc[category] = [];
    acc[category].push(curr);
    return acc;
  }, {});
}

function groupByCategories(data, groups = [], acc = {}){
  if(!data.length) return [];
  
  groups = groups.filter(ele =>{
    return ele in data[0];
  });
  
  if(!groups.length) return data;
  
  var groupCopy = Object.assign([], groups);
  var groupedData = groupByCategory(data, groupCopy.shift());
  var groupedDataKeys = Object.keys(groupedData);
  var children = groupedDataKeys.map(el => {
    return groupedData[el];
  });
  for(var i = 0; i < children.length; i++){
    acc[groupedDataKeys[i]] = groupCopy.length ? {} : [];
    acc[groupedDataKeys[i]] = groupByCategories(children[i], groupCopy, acc[groupedDataKeys[i]]);
  }
  
  return acc;
}

function createColumnHeaders(data, cols = [], firstColumn = ''){
  if(!cols.length) return {columnHeaders: [firstColumn], mapToheader: 1};
  
  var groupedData = groupByCategories(data, cols);
  var columnHeaders = [];
  var mapToHeader = Object.assign({}, groupedData);
  var mapPos = 1;
  
  (function columnHeaderRecursion(data, pos = 0, headerMap){
    if(typeof data !== 'object' || Array.isArray(data)) return 1;
    else{
      var currKeys = Object.keys(data);
      var reqLength = 0;
      for(var i = 0; i < currKeys.length; i++){
        var currLength = columnHeaderRecursion(data[currKeys[i]], pos + 1, headerMap[currKeys[i]]);
        if(Array.isArray(data[currKeys[i]])){
          headerMap[currKeys[i]] = mapPos;
          mapPos += 1;
        }
        reqLength += currLength;
        columnHeaders[pos] = !columnHeaders[pos] ? [firstColumn].concat(Array(currLength).fill(currKeys[i])) : columnHeaders[pos].concat(Array(currLength).fill(currKeys[i]));
      }
      return reqLength;
    }
  })(groupedData, 0, mapToHeader);
  
  return {
    columnHeaders,
    mapToHeader
  };
}

// console.log('headers', createColumnHeaders(data, ['borough', 'gender'], 'Table'));


//accumulator has two different signatures
//1. it takes an array of objects, an accumulation category as a string (like age), and supported accumulation type as a string (like count)
//2. it takes an array of objects, a callback function (which operates the same as reduce), and an initial value
function accumulator(arr, accCat, accType, accValue){
  if(!accCat && typeof accType !== 'function') accType = 'count';
  else if(typeof accCat === 'function'){
    accValue = accType;
    accType = accCat;
  }
  
  return arr.reduce((acc, curr, index, array) => {
    if(typeof accType === 'function'){
      return accType(acc, curr, index, array); 
    }
    switch(accType){
      case('sum'):{
        acc += parseInt(curr[accCat]);
        return acc;
      }
      
      case('count'):{
        acc += 1;
        return acc;
      }
      
      default:{
        acc += 1;
        return acc;
      }
    }
  }, accValue || 0);
}

// accumulator(data, 'age', 'sum')<commit_msg>Changing parseInt to Number to properly calculate scientific numbers<commit_after>// var data = [
//   ['name', 'borough', 'age', 'gender'],
//   ['patrick', 'brooklyn', '28', 'm'],
//   ['greg', 'brooklyn', '29', 'm'],
//   ['niles', 'manhattan', '30', 'm'],
//   ['jared', 'manhattan', '29', 'm'],
//   ['markus', 'manhattan', '28', 'm'],
//   ['sarah', 'queens', '30', 'f'],
//   ['vishakh', 'queens', '28', 'm'],
//   ['jessica', 'brooklyn', '28', 'f']
// ];

var data = [
  {name: 'patrick', borough: 'brooklyn', age: '28', gender: 'm'},
  {name: 'greg', borough: 'brooklyn', age: '29', gender: 'm'},
  {name: 'niles', borough: 'manhattan', age: '30', gender: 'm'},
  {name: 'jared', borough: 'manhattan', age: '29', gender: 'm'},
  {name: 'markus', borough: 'manhattan', age: '28', gender: 'm'},
  {name: 'sarah', borough: 'queens', age: '30', gender: 'f'},
  {name: 'vishakh', borough: 'queens', age: '28', gender: 'm'},
  {name: 'jessica', borough: 'brooklyn', age: '28', gender: 'f'}
];

function groupByCategory(data, groupBy){
  return data.reduce((acc, curr) =>{
    var category = curr[groupBy]
    if(!acc[category]) acc[category] = [];
    acc[category].push(curr);
    return acc;
  }, {});
}

function groupByCategories(data, groups = [], acc = {}){
  if(!data.length) return [];
  
  groups = groups.filter(ele =>{
    return ele in data[0];
  });
  
  if(!groups.length) return data;
  
  var groupCopy = Object.assign([], groups);
  var groupedData = groupByCategory(data, groupCopy.shift());
  var groupedDataKeys = Object.keys(groupedData);
  var children = groupedDataKeys.map(el => {
    return groupedData[el];
  });
  for(var i = 0; i < children.length; i++){
    acc[groupedDataKeys[i]] = groupCopy.length ? {} : [];
    acc[groupedDataKeys[i]] = groupByCategories(children[i], groupCopy, acc[groupedDataKeys[i]]);
  }
  
  return acc;
}

function createColumnHeaders(data, cols = [], firstColumn = ''){
  if(!cols.length) return {columnHeaders: [firstColumn], mapToheader: 1};
  
  var groupedData = groupByCategories(data, cols);
  var columnHeaders = [];
  var mapToHeader = Object.assign({}, groupedData);
  var mapPos = 1;
  
  (function columnHeaderRecursion(data, pos = 0, headerMap){
    if(typeof data !== 'object' || Array.isArray(data)) return 1;
    else{
      var currKeys = Object.keys(data);
      var reqLength = 0;
      for(var i = 0; i < currKeys.length; i++){
        var currLength = columnHeaderRecursion(data[currKeys[i]], pos + 1, headerMap[currKeys[i]]);
        if(Array.isArray(data[currKeys[i]])){
          headerMap[currKeys[i]] = mapPos;
          mapPos += 1;
        }
        reqLength += currLength;
        columnHeaders[pos] = !columnHeaders[pos] ? [firstColumn].concat(Array(currLength).fill(currKeys[i])) : columnHeaders[pos].concat(Array(currLength).fill(currKeys[i]));
      }
      return reqLength;
    }
  })(groupedData, 0, mapToHeader);
  
  return {
    columnHeaders,
    mapToHeader
  };
}

// console.log('headers', createColumnHeaders(data, ['borough', 'gender'], 'Table'));


//accumulator has two different signatures
//1. it takes an array of objects, an accumulation category as a string (like age), and supported accumulation type as a string (like count)
//2. it takes an array of objects, a callback function (which operates the same as reduce), and an initial value
function accumulator(arr, accCat, accType, accValue){
  if(!accCat && typeof accType !== 'function') accType = 'count';
  else if(typeof accCat === 'function'){
    accValue = accType;
    accType = accCat;
  }
  
  return arr.reduce((acc, curr, index, array) => {
    if(typeof accType === 'function'){
      return accType(acc, curr, index, array); 
    }
    switch(accType){
      case('sum'):{
        acc += Number(curr[accCat]);
        return acc;
      }
      
      case('count'):{
        acc += 1;
        return acc;
      }
      
      default:{
        acc += 1;
        return acc;
      }
    }
  }, accValue || 0);
}

// accumulator(data, 'age', 'sum')
{'fraction_non_alphanumeric': 0.19232212079529823, 'fraction_numerical': 0.01200450168813305, 'mean_word_length': 3.455710306406685, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1574899', 'n_tokens_mistral': 2784, 'n_tokens_neox': 2681, 'n_words': 693}