sorry for the lack of code — but this is a pretty straight forward javascript question.
I need to sort through an array of objects and push objects with the same key/value pair to a new array.
The objects i'm sorting through are football players. I have all players in an array of objects and want to push each position into their own array.
Ex. each object has a key of "position" so any object with the value of "QB" for the key "position" should be pushed into a new array "Quarterbacks".
What's the right way to sort through objects and push them into a new array that would work for my scenario?
You can use Array.prototype.forEach:
var data = [
{ position: "quarterback", name: "Bob" },
{ position: "center", name: "Jim" },
{ position: "quarterback", name: "Ted" },
];
var dataByPositions = {};
data.forEach(function(x) {
dataByPositions[x.position] = dataByPositions[x.position] || [];
dataByPositions[x.position].push(x);
});
// Demonstration purposes only:
document.body.innerHTML = "<pre>" + JSON.stringify(dataByPositions, null, 4) + "</pre>";
You can create an index object that contains all the positions you've seen so far and then accumulate an array of players for each position:
var data = [
{position: "quarterback", name: "Bob"},
{position: "center", name: "Jim"},
{position: "quarterback", name: "Ted"},
];
var positions = {};
data.forEach(function(item) {
if (!(item.position in positions)) {
positions[item.position] = [];
}
positions[item.position].push(item);
});
log(positions);
function log(x) {
var div = document.createElement("div");
div.innerHTML = JSON.stringify(x);
document.body.appendChild(div);
}
Or you could make this into a generic grouping function like this:
var data = [
{position: "quarterback", name: "Bob"},
{position: "center", name: "Jim"},
{position: "quarterback", name: "Ted"}
];
function groupData(data, key) {
var results = {};
data.forEach(function(item) {
console.log(item);
var value = item[key];
if (!(value in results)) {
results[value] = [];
}
results[value].push(item);
});
return results;
}
log(groupData(data, "position"));
function log(x) {
var div = document.createElement("div");
div.innerHTML = JSON.stringify(x);
document.body.appendChild(div);
}
Related
Given the following arrays:
var ids = [1,2,3]; //Hundreds of elements here
var names = ["john","doe","foo"]; //Hundreds of elements here
var countries = ["AU","USA,"USA"]; //Hundreds of elements here
What's the best way performance-wise to generate an array of objects with a similar structure to this:
var items = [
{id:1,name:"john",country:"AU"},
{id:2,name:"doe",country:"USA"},
...
];
You should be able to simply map through all ids, keeping a reference to your index, and build your object based on that index.
var items = ids.map((id, index) => {
return {
id: id,
name: names[index],
country: countries[index]
}
});
This is what I get when run the code:
[
{ country=AU, name=john, id=1.0 },
{ name=doe, country=USA, id=2.0 },
{ id=3.0, country=USA, name=foo }
]
Following is the code, same as #Zack Tanner
function arrs2Obj() {
var ids = [1, 2, 3]; //Hundreds of elements here
var names = ["john", "doe", "foo"]; //Hundreds of elements here
var countries = ["AU", "USA", "USA"]; //Hundreds of elements here
var items = ids.map((id, index) => {
return {
id: id,
name: names[index],
country: countries[index]
}
});
Logger.log(items)
}
The problem is, this result is not sorted as the questioner asked. I mean it's not consistent - ids to be the first item, name second, country 3rd; this way it is more presentable.
I'm trying to collate some data. I would like to populate an array containing sub arrays, for example, I have some json data that I am iterating over:
{
"name": "name1",
"prices": "209.67"
},
{
"name": "name1",
"prices": "350"
},
{
"name": "name2",
"price": "195.97"
},
I would like to create an array that ends up looking something like the following:
myArray['name1']prices[0] = 209.67,
prices[1] = 350,
['name2']prices[0] = 195.97
I thought that the code below would achieve what I wanted but it doesn't work. It throws an exception. It doesn't seem to recognise the fact that the prices are an array for a given index into the main array. Instead the prices appear at the same level as the names. I want the main array for a given name to contain an inner array of prices.. Does anybody have any idea how I could modify to make this work?
function doStuff() {
var cryptoData = getData();
var datasetValues = {};
datasetValues.names = [];
datasetValues.names.prices = [];
for (var result = 0; result < cryptoData.length; result++) {
var data = cryptoData[result];
if (datasetValues.names.indexOf(data.cryptoname) === -1)
{
datasetValues.names.push(data.cryptoname);
}
// This works
//datasetValues.names.prices.push(data.prices);
// This doesn't!
datasetValues.cryptoNames[data.cryptoname].prices.push(data.prices);
}
}
You could reduce the array by using an object and take a default object if the property is not set. Then push the price.
var data = [{ name: "name1", price: "209.67" }, { name: "name1", price: "350" }, { name: "name2", price: "195.97" }],
result = data.reduce((r, { name, price }) => {
r[name] = r[name] || { name, prices: [] };
r[name].prices.push(+price);
return r;
}, Object.create(null));
console.log(result);
Try this
function parseData(input){
return input.reduce(function(o,i){
o[i.name] = {};
if(!o[i.name]['prices']){
o[i.name]['prices'] = [];
}
o[i.name]['prices'].push(i.prices);
return o;
},{});
}
My object:
"hockey": {
stats: {
skaters: {
regular: [
{name: "stat1", key: "statkey1"}
{name: "stat2", key: "statkey2"}
{name: "stat3", key: "statkey3"}
]
},
goalies: {
regular: [
{name: "stat1", key: "statkey4"}
{name: "stat2", key: "statkey5"}
{name: "stat3", key: "statkey6"}
]
}
}
}
My code:
var stats = [];
var key = "";
for (position in sport.stats) {
for (stat_group in position) {
for (stat in stat_group) {
key = stat.key;
stats[key] = true;
}
}
}
I'm trying to use the above code to grab the property key from each object located within sport.stats.position.stat_group. Each sport has a different number of positions and stat groups, hence the triple for loop. I'm not getting any console errors it just isn't grabbing the key at all and the iterator variables aren't evaluating to objects but integers.
Here's what I want the resulting stats object to be:
{
"statkey1": true,
"statkey2": true,
"statkey3": true,
...
}
Hope you guys can help! Thanks!
The JS for...in loop iterates through the keys, not values. If you want to iterate an object fully, you can do so like this:
for (key in sports.stats) {
var position = sports.stats[key];
for (group_key in position) {
var stat_group = position[group_key];
for (stat_key in stat_group) {
stat_group[stat_key] = true;
}
}
}
For...in in javascript gives you the key of the object, not the value.
According to your logic, this is what you meant to do:
var stats = {};
var key = "";
for (position in sport.stats) {
for (stat_group in sport.stats[position]) {
for (stat in sport.stats[position][stat_group]) {
key = sport.stats[position][stat_group][stat].key;
stats[key] = true;
}
}
}
If I have an array of objects like this:
var mountains = [
{ name: 'Kebnekaise', elevation: 2106 },
{ name: 'Mount Ngauruhoe', elevation: 2291, comment: 'aka Mount Doom' }
];
How to get all unique keys i.e. ['name', 'elevation', 'comment']?
In ECMAScript 2015, it's really simple:
let mountains = [
{ name: 'Kebnekaise', elevation: 2106 },
{ name: 'Mount Ngauruhoe', elevation: 2291, comment: 'aka Mount Doom' }
];
let uniqueKeys = Object.keys(Object.assign({}, ...mountains));
Using ES6, one could do
var unique = new Set([].concat.apply([],mountains.map(Object.keys)))
Without ES6, something like
var unique = [].concat.apply([],mountains.map(Object.keys)).filter(function(value,i,arr) {
return arr.indexOf(value) === i;
});
You could iterate over the array of objects and iterate over each element's keys with Object.keys(obj), adding them to a hash to avoid duplicates:
function getKeySet (data) {
var keys = {};
data.forEach(function (datum) {
Object.keys(datum).forEach(function (key) {
keys[key] = true;
});
});
return Object.keys(keys);
}
Alternately you could add all the keys to an array and filter out duplicates. Either way this will be O(nm) where n is the number of elements in the array and m is the average number of keys.
I have a series of JSON entries:
[{"num": "1","name_A": "Alex" ,"name_B": "Bob"}, {"num": "2","name_A": "Anne" ,"name_B": "Barbra"}]
I am trying to convert this array of Objects as painlessly as possible into two objects - one with title name_A, and the second with the title name_B. Objects have to contain the title and an array of matching num-name pairs:
[{title: "name_A", names:[{"1", "Alex}, {"2", "Anne"}]}, {title:"name_B", names: [{"1", "Bob"}, {"2", "Barbra"}]}]
At first I tried simply to create two objects by reducing the array of object twice, once for name_A and second time for name_B and later glue everything together:
// get 'names' array
var name_A = objArray.reduce(function(memo, curr) {
memo.push({curr.num, curr.name_A})
return memo;
}, []);
But even this is failing. Why there is no push method for memo if I initialize reduce with an empty array?
And second question, am I on a right track or is there a better way to achieve this?
Comments inline, made a few minor corrections to the expectations.
var input = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }]
var output = input.reduce(function (a, b) {
// construct new objects and set their properties
var i = {};
i[b.num] = b.name_A;
var j = {};
j[b.num] = b.name_B;
// add them to our collection elements
a[0].names.push(i);
a[1].names.push(j);
return a;
// initializing our collection
}, [{ title: "name_A", names: [] }, { title: "name_B", names: [] }]);
// pretty print our output
console.log(JSON.stringify(output, null, " "))
var input = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }]
var output = input.reduce(function (a, b) {
// construct new objects and set their properties
var i = {};
i[b.num] = b.name_A;
var j = {};
j[b.num] = b.name_B;
// add them to our collection elements
a[0].names.push(i);
a[1].names.push(j);
return a;
// initializing our collection
}, [{ title: "name_A", names: [] }, { title: "name_B", names: [] }]);
so.log(output)
<pre id="output"></pre>
<script>
var so = {
log: function(o) {
document.getElementById("output").innerHTML = JSON.stringify(o, null, " ")
}
}
</script>
The problem with your code is that { curr.num, curr.name_A } is not a valid object, it's missing the property names. I've added properties num and name in my code below.
var name_A = [];
var name_B = [];
objArray.forEach(function(curr) {
name_A.push({num: curr.num, name: curr.name_a});
name_B.push({num: curr.num, name: curr.name_B});
});
var result = [
{ title: "name_A" }, names: name_A },
( title: "name_B" }, names: name_B }
];
Also, if you want to make an array out of the results of looping over an array, you should use .map rather than .reduce.
Assuming only property num is fixed. All other properties are treated as data, like name_A or name_B.
var a = [{ "num": "1", "name_A": "Alex", "name_B": "Bob" }, { "num": "2", "name_A": "Anne", "name_B": "Barbra" }],
result = [];
a.forEach(function (el) {
var num = el.num;
Object.keys(el).forEach(function (k) {
function tryFindIndexAndSetNames(aa, i) {
if (aa.title === k) {
result[i].names[num] = el[k];
return true;
}
}
if (k !== 'num' && !result.some(tryFindIndexAndSetNames)) {
var o = {};
o[num] = el[k];
result.push({ title: k, names: o });
}
});
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');