4

I'm making a call to an API and getting an array with large amounts of objects. There are hundreds of objects in the array and a short snippet of it looks something like this:

[
    {
      "name": "total_kills_glock",
      "value": 70
    },
    {
      "name": "total_kills_mac10",
      "value": 39
    },
    {
      "name": "total_kills_ump45",
      "value": 136
    },
    {
      "name": "total_shots_glock",
      "value": 1262
    },
    {
      "name": "total_hits_glock",
      "value": 361
    }
    {
      "name": "total_shots_mac10",
      "value": 862
    },
    {
      "name": "total_hits_mac10",
      "value": 261
    },
    {
      "name": "total_shots_ump45",
      "value": 1610
    },
    {
      "name": "total_hits_ump45",
      "value": 598
    }
]

Is there a way to sort the array using regex to look something like this:

[
  {
    "name": "glock",
    "kills": 70,
    "shots": 1262,
    "hits": 361
  },
  {
    "name": "mac10",
    "kills": 39,
    "shots": 862,
    "hits": 261
  },
  {
    "name": "ump45",
    "kills": 136,
    "shots": 1610,
    "hits": 598
  }
]
3
  • 2
    You should try it first and share your code instead of directly stating requirements. I don’t see an attempt made to solve this. Commented Oct 6, 2019 at 4:05
  • Ah, that is my bad, I didn't put the code that I've created so far. I only post when I know I tried working on a solution for hours and still can't come up with an answer. Commented Oct 6, 2019 at 4:24
  • @Leon kindly check my answer out stackoverflow.com/a/58254128/12167785 Commented Oct 6, 2019 at 5:17

6 Answers 6

4

Here's one way to do it, using regex to extract the name and data type from the raw name, and then building an object based on that:

const raw = [{
    "name": "total_kills_glock",
    "value": 70
  },
  {
    "name": "total_kills_mac10",
    "value": 39
  },
  {
    "name": "total_kills_ump45",
    "value": 136
  },
  {
    "name": "total_shots_glock",
    "value": 1262
  },
  {
    "name": "total_hits_glock",
    "value": 361
  },
  {
    "name": "total_shots_mac10",
    "value": 862
  },
  {
    "name": "total_hits_mac10",
    "value": 261
  },
  {
    "name": "total_shots_ump45",
    "value": 1610
  },
  {
    "name": "total_hits_ump45",
    "value": 598
  }
];

var final = [];
var keys = [];
raw.forEach(v => {
  const m = v.name.match(/^total_([^_]+)_(.+)$/);
  const k = keys.indexOf(m[2]);
  if (k == -1) {
    var o = { name: m[2] };
    o[m[1]] = v.value;
    final.push(o);
    keys.push(m[2]);
  } else {
    final[k][m[1]] = v.value;
  }
});
console.log(final);

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

Comments

4

You can use reduce() method to group the array items, split() method to extract name and action from name string.

var data = [{ "name": "total_kills_glock", "value": 70 }, { "name": "total_kills_mac10", "value": 39 }, { "name": "total_kills_ump45", "value": 136 }, { "name": "total_shots_glock", "value": 1262 }, { "name": "total_hits_glock", "value": 361 }, { "name": "total_shots_mac10", "value": 862 }, { "name": "total_hits_mac10", "value": 261 }, { "name": "total_shots_ump45", "value": 1610 }, { "name": "total_hits_ump45", "value": 598 } ];

var result = data.reduce((acc, curr) => {
  let words = curr.name.split('_');
  let name = words[2];
  let action = words[1];

  let item = acc.find(item => item.name === name);

  if (item) {
    item[action] = curr.value;
  } else {
    acc.push({
      "name": name,
      [action]: curr.value
    });
  }

  return acc;
}, []);

console.log(result);

2 Comments

That's works really well! I thought that the only solution would be to use regex, but I was wrong. I'm going to apply this to other parts of my application.
That's nice to hear @Leon. Regex of course works, but split() would be enough and suits perfectly for this simple problem.
1

Kindly check this out.

let arr=[] //your array
let map=new Map();
arr.forEach((obj)=>{
let x=obj.name.split('_');
map.set(x[2],{"name":x[2],...map.get(x[2]),[x[1]]:obj.value})
}
);
console.log([...map.values()]);

Feel free to use my repl https://repl.it/repls/PoisedEasyZettabyte in case you want it.

Comments

1

You could split the string and destructure the items and take an object as hash table for the same group. At the end take the values from the hash table.

var data = [{ name: "total_kills_glock", value: 70 }, { name: "total_kills_mac10", value: 39 }, { name: "total_kills_ump45", value: 136 }, { name: "total_shots_glock", value: 1262 }, { name: "total_hits_glock", value: 361 }, { name: "total_shots_mac10", value: 862 }, { name: "total_hits_mac10", value: 261 }, { name: "total_shots_ump45", value: 1610 }, { name: "total_hits_ump45", value: 598 }],
    result = Object.values(data.reduce((r, { name, value }) => {
        var [, type, name] = name.split('_');
        r[name] = r[name] || { name };
        r[name][type] = value;
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

Below is a pure functional solution, which combines a couple of useful techniques. Note, that you can re-use partial functions somewhere else in the code. Also, this way the calculation can be parallelized and distributed (although JavaScript is not there yet).

 let raw = [
    {"name": "total_kills_glock", "value": 70},
    {"name": "total_kills_mac10", "value": 39},
    {"name": "total_kills_ump45", "value": 136},
    {"name": "total_shots_glock", "value": 1262},
    {"name": "total_hits_glock", "value": 361},
    {"name": "total_shots_mac10", "value": 862},
    {"name": "total_hits_mac10", "value": 261},
    {"name": "total_shots_ump45", "value": 1610},
    {"name": "total_hits_ump45", "value": 598}
];

//you can use any parsing technique here, including regexp
const parse = name => { 
    const a = name.split("_"); 
    return { key: a[2], metric: a[1] }
};

const groupBy = getKey => (rv, x) => {
    (rv[getKey(x)] = rv[getKey(x)] || []).push(x);
    return rv;
};

//converts [{key,metric,value},...] to {name:key, m1:v1, m2:v2,...} 
const recombine = arr => Object.assign({name: arr[0].key},
    ...Array.from(arr, ({key, metric, value}) => ({[metric]: value}) ));

const groupedByKey = raw
            .map( ({name, ...attrs}) => ({...parse(name), ...attrs}))
            .reduce(groupBy(x => x.key),{});

const result = Object.values(groupedByKey).map(recombine);
console.log(result);

I did not benchmark it, but I would not be surprised, if it performed better than Nick's solution with certain datasets. That said, it might not be as easy to maintain, depending on the team and skills.

Comments

0

Perhaps a loop would help a bit more then using regex to create a new object, not sure I've ever seen that before.

var rawData = [ { "name": "total_kills_glock", "value": 70 }, { "name": "total_kills_mac10", "value": 39 }, { "name": "total_kills_ump45", "value": 136 }, { "name": "total_shots_glock", "value": 1262 }, { "name": "total_hits_glock", "value": 361 }, { "name": "total_shots_mac10", "value": 862 }, { "name": "total_hits_mac10", "value": 261 }, { "name": "total_shots_ump45", "value": 1610 }, { "name": "total_hits_ump45", "value": 598 } ];

var gunStats = [{
    "name": "glock",
    "kills": 0,
    "shots": 0,
    "hits": 0
},
{
    "name": "mac10",
    "kills": 0,
    "shots": 0,
    "hits": 0
  },
  {
    "name": "ump45",
    "kills": 0,
    "shots": 0,
    "hits": 0
  }
];

rawData.forEach((item)  => { 
    if (item.name.includes("glock")) {
        applyStatsToGun(item, "glock");
    }

    if (item.name.includes("mac10")) {
        applyStatsToGun(item, "mac10");
    }

    if (item.name.includes("ump45")) {
        applyStatsToGun(item, "ump45");
    }

});


function applyStatsToGun(item, gunName) {
    if (item.name.includes(gunName)) {

        if (item.name.includes("kills")) {
            var gun = gunStats.find((gun) => gun.name == gunName);
            gun.kills += item.value;
        }

        if (item.name.includes("shots")) {
            var gun = gunStats.find((gun) => gun.name == gunName);
            gun.shots += item.value;
        }

        if (item.name.includes("hits")) {
            var gun = gunStats.find((gun) => gun.name == gunName);
            gun.hits += item.value;
        }
    }
}


console.log(gunStats);

And if you don't know want to hard code the gun name strings, say there's hundreds first you'd want to get a list of all unique names iterating through the list to and parse the 3rd element in name.split("_")[3] then iterating through that list and applying the value instead of hard coding those values

1 Comment

Thank you, this works great too! This was similar to the initial code that I wrote, but couldn't figure out why it wasn't working. I'm learning so much from this code example that you wrote!

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.