Matt Furr
Matt Furr

Reputation: 53

JavaScript to split data and calculate sums

I believe what I need are two JavaScript functions. I am receiving a comma separated string that holds two types of data: 1) device name followed by 2) numeric value. These two values are separated by a comma, and each set is also separated by a comma. Example string below:

Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7

What I want to do is create two separate functions. The first function finds the unique device names and returns just the names in a comma separated string. The second function would calculate the sum of the numeric values for each device. The expected results from the example string above would return:

Function 1 (Device List):

Device_A, Device_B, Device_C

Function 2 (Sums per Device List):

15,10,9

The lists do not need to return in any particular order as long at they both match up. All I have successfully done at this point is return a list of unique values (including numeric values)... I'm stuck on separating the list, but still referring to device name to sum up all of the values.

Thanks in advance. Let me know if you have any questions! Matt

Upvotes: 3

Views: 634

Answers (4)

Nina Scholz
Nina Scholz

Reputation: 386600

You could use an object for collecting the names and count.

This edit contains a shared function and two function for the result in equal order.

function getGrouped(data) {
    var array = data.split(','),
        temp = Object.create(null),
        i = 0;
    
    while (i < array.length) {
        temp[array[i]] = (temp[array[i]] || 0) + +array[i + 1] || 0;
        i += 2;
    }
    return temp;
}

function getDevices(data) {
    var temp = getGrouped(data);
    return Object.keys(temp).sort().join();
}

function getCounts(data) {
    var temp = getGrouped(data);
    return Object.keys(temp).sort().map(function (k) { return temp[k]; }).join();
}

var data = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";

console.log(getDevices(data));
console.log(getCounts(data));

Upvotes: 3

user3297291
user3297291

Reputation: 23372

When starting out on a problem like this, I think it's wise to not worry about doing it in a single loop or in a fancy one-liner at first.

A) Start out by defining what data structures you need and how to go from one format to another:

  • Convert my string of data to a list of keys and values
  • Somehow group these keys and values based on the key
  • Sum the values for each group
  • Return a list of all unique keys
  • Return a list of all summed values

B) Then, try to see if any of the code you've written has the potential be re-used by other parts of your application and refactor accordingly.

C) Finally, assess if there are performance bottle necks and only if there are, optimize for performance.

A. A function for each step:

// 1. From string to array of keys and values
// You already figured this one out. Split by ","!
const namesAndValuesFromString =
  str => str.split(",");
  
// 2. Grouping by key
// Let's first make pairs:
const deviceValuePairs = devicesAndValues => { 
  let pair = [];
  const pairs = [];
  
  devicesAndValues.forEach(x => {
    pair.push(x);
    if (pair.length === 2) {
      pairs.push(pair);
      pair = [];
    }
  });
  
  return pairs;
};

// Key value pairs are a nice starting point for constructing a grouped object:
const kvpsToDeviceValuesObj = kvps => {
  const valuesByDevice = {};
  
  kvps.forEach(([key, value]) => {
    value = Number(value);
    
    if (!valuesByDevice[key]) {
      valuesByDevice[key] = [];
    }
    
    valuesByDevice[key].push(value);
  });
  
  return valuesByDevice;
};

// 3. Now, we can get to summing the values arrays
const sumValueArrays = valuesByDevice => {
  const summedValuesByDevice = {};
  
  // Loop over the objects entries
  Object.entries(valuesByDevice).forEach(
    ([key, values]) => {
      summedValuesByDevice[key] = values
        .reduce((a, b) => a + b);
    }
  );
  
  return summedValuesByDevice;
};

// 4. + 5. Now that we have an object with device ids as keys, and summed values inside, we can retrieve the two lists
const getDevices = Object.keys;
const getSums = Object.values;

// Running the code:
const namesAndValues =
  namesAndValuesFromString("A,5,C,2,A,10,B,8,B,2,C,7");
console.log(namesAndValues);

const kvps = deviceValuePairs(namesAndValues);
console.log(kvps);

const valuesByDevice = kvpsToDeviceValuesObj(kvps);
console.log(valuesByDevice);

const sumValues = sumValueArrays(valuesByDevice);
console.log(sumValues);

const devices = getDevices(sumValues);
console.log(devices);

const sums = getSums(sumValues);
console.log(sums);

B. Refactoring!

Once you understand each of those steps, you'll start to see things that can be generalized or combined. That's where the fun starts :)

// UTILITIES
const split = del => arr => arr.split(del);
const toPairs = arr => {
  let pair = [];
  
  return arr.reduce(
    (pairs, x) => {
      pair.push(x);
      if (pair.length === 2) {
        pairs.push(pair);
        pair = [];
      }
      
      return pairs;
    }, []);
};

const sum = (x, y = 0) => +x + y;

const kvpsToGroups = grouper => kvps =>
  kvps.reduce(
    (groups, [key, value]) => Object.assign(groups, {
      [key]: grouper(value, groups[key])
    }), {});


// YOUR APP
const sumGrouper = kvpsToGroups(sum);
const dataSplitter = split(",");

const parseData = str => sumGrouper(toPairs(dataSplitter(str)));

// MAIN
const result = parseData("A,5,C,2,A,10,B,8,B,2,C,7");
console.log("devices:", Object.keys(result));
console.log("sums:", Object.values(result));

Upvotes: 1

Marc Laval
Marc Laval

Reputation: 3

Something like this should do it:

var input = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";

var output = input.split(',').reduce((accumulator, currentValue, currentIndex, array) => {
	accumulator[currentValue] = (accumulator[currentValue] || 0) 
    + parseInt(array[currentIndex + 1]);
	array.splice(0,1);
	return accumulator;
}, {});

console.log(Object.keys(output));
console.log(Object.keys(output).map(k => output[k]));

Upvotes: 0

raksa
raksa

Reputation: 938

another way by regexs

let str = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7", obj = {}
str.match(/(\w+,[0-9]+)/g).forEach((s) => {
	s = s.split(',')
	obj[s[0]] = (obj[s[0]] || 0) + (Number(s[1]) || 0)
})
console.log(obj)

Upvotes: 0

Related Questions