I am trying to get my array of objects in the format that is needed to create a C3 bar chart but I am having trouble categorizing my data with JavaScript. Below is my JavaScript code.
data = [
{Service:"Army",Permanent:20,Itinerant:754,Region:"Western"},
{Service:"Air Force",Permanent:100,Itinerant:2,Region:"Eastern"},
{Service:"Army",Permanent:10,Itinerant:7,Region:"Western"},
{Service:"Air Force",Permanent:30,Itinerant:2,Region:"Eastern"}
]
var sumAry=[];
for (var x=0; x<data.length; x++){
var val =sumAry.indexOf(data[x].Service);
if(val === -1){
var permanent += data[x].Permanent;
sumAry.push(data[x].Service, permanent);
}else{
console.log("IN");
}
}
https://codepen.io/isogunro/pen/GYRKqE?editors=0012
The C3 chart looks for a data in the structure/format shown below:
['Army', 30],
['Airorce', 130],
['Navy', 190],
['Army1', 70],
['Airorce2', 130],
['Navy3', 100]
For each of the values, the 'Permanent' property will be added up to get the number part of the array. It becomes an aggregate of all the information.
Assuming the number in the preferred format comes from the Permanent property, you could use Array.map to transform your dataset.
var data = [{
Service: "Army",
Permanent: 654,
Itinerant: 754,
Region: "Western"
},
{
Service: "Air Force",
Permanent: 9,
Itinerant: 2,
Region: "Eastern"
},
{
Service: "Army",
Permanent: 6,
Itinerant: 7,
Region: "Western"
},
{
Service: "Air Force",
Permanent: 9,
Itinerant: 2,
Region: "Eastern"
}
];
var aggregates = data.map(function (o) {
// map original objects to new ones with zeroed-out data
return {
Service: o.Service,
Permanent: 0,
}
}).filter(function (o, index, a) {
// filter the list to have only unique `Service` properties
var service = o.Service;
var i;
for (i = 0; i < a.length; i += 1) {
if (a[i].Service === service) {
// break out of the loop when the first matching `Service` is found.
break;
}
}
// `i` is now the index of the first matching `Service`.
// if it's the first occurrence of that `Service`, keep it, otherwise ditch it.
return i === index;
});
data.forEach(function (o) {
// loop through the aggregate list and get the matching `Service` property.
var agg = aggregates.filter(function (p) {
return o.Service === p.Service;
})[0]; // first element is the match.
// sum the `Permanent` properties.
agg.Permanent += o.Permanent;
});
// now that the data has been aggregated, transform it into the needed structure.
var c3data = aggregates.map(function (d) {
return [d.Service, d.Permanent];
});
console.log(JSON.stringify(c3data));
Related
I want to return an array containing all the elements of the array located at the given key that are equal to ten. It should print out [10, 10] but the result of my code is [ 10, 10 ] with extra space in the front and back or my code is just wrong.
var obj = {
key: [1000, 10, 50, 10],
key2: [],
key3: "abc"
};
function isValid(obj, key) {
var result = [];
if (!obj.hasOwnProperty("key") ||
!Array.isArray(obj[key]) ||
obj[key].length === 0) {
return []; //return empty array if those condition meet.
}
else {
for (var i = 0; i < obj[key].length; i++) {
if (obj[key][i] === 10) {
result.push(obj[key][i]); //push the 10 to result empty array.
}
}
return result;
}
}
var output = isValid(obj, 'key');
console.log(output); // --> should print out [10, 10]
It may be a problem with your browser. Space seperating contents in an array automatically removed when you iterate through it. Some browsers add a space between each entry for visual improvement. As you are using a number array, the space will not affect your normal code execution.
I have this source table data:
And I would like to get data parsed like this (grouped by row):
I grouped source table into an array of objects.
This is my array:
[ { rowId: 4, colId: 10 } { rowId: 4, colId: 11 } .... ]
Now I would like to get an array of parsed objects..
How can I do this?
I parsed the array with a for loop but I got some error when I create the new array of objects..
My code:
for (var i=0; i<tableArray.length; i++) {
if (tableArray[i].rowId != rowLast) {
bar = true;
var row = tableArray[i].rowId;
var start = tableArray[i].colId;
}
if ((bar)&&(tableArray[i].colId != colLast)) {
var end = tableArray[i].colId;
tab = { row: row, start: start, end: end }
newTableArray.push(tab);
bar = false;
}
rowLast = tableArray[i].rowId;
colLast = tableArray[i].colId;
}
Help! I'm a bit confused in loop :(
Many thanks.
You could group the elements and use an object for the last values. This solution needs sorted data.
var array = [{ rowId: 4, colId: 10 }, { rowId: 4, colId: 11 }, { rowId: 4, colId: 12 }, { rowId: 4, colId: 20 }, { rowId: 4, colId: 21 }, { rowId: 6, colId: 6 }, { rowId: 6, colId: 7 }, { rowId: 6, colId: 8 }, { rowId: 7, colId: 12 }, ],
group = [];
array.forEach(function (a, i) {
if (!i || // group changes if first object i = 0
this.last.row !== a.rowId || // or different rowId
this.last.end + 1 !== a.colId // or not in sequence
) {
this.last = { row: a.rowId, start: a.colId, end: a.colId };
group.push(this.last);
}
this.last.end = a.colId;
}, {});
console.log(group);
I would rather write a function to generate the new array, I hope the comments explain the thought process behind it:
function transform(array) {
var output = [];
// initiates the first object you want in your output array
// with the row and colId of the first object from the input array
var obj = {
row: array[0].row,
start: array[0].colId,
end: array[0].colId
};
// Loop starts at 1 instead of 0 because we used the first object array[0] already
for (var i = 1; i < array.length; i++) {
var current = array[i];
// if the current objects row is still the same,
// AND the colId is the next colId (meaning no spare cols between)
// set the new objects end to this colId
if(obj.row === current.row && (current.colId - obj.end) === 1 ){
obj.end = current.colId;
}
// when the row does not match, add the object to the output array and
// re-innitiate it with the current objects row and colId
else {
output.push(obj);
obj.row = current.row;
obj.start = current.colId;
obj.end = current.colId;
}
}
// Once the loop is done, add the last remaining object to the output array
output.push(obj);
return output;
}
I want to store all duplicates and unique values of suits and values of an array of Card objects. Each card has a suit and value property. The datastructure looks like this:
cards = [
{
suit: 'spades',
value : 4
},
{
suit: 'hearts',
value : 4
},
{
suit: 'spades',
value : 11
},
{
suit: 'spades',
value : 12
}
etc...
]
I'm trying to use array.reduce to check and store duplicates and unique values and suits for both, but am having trouble structuring the code.
Rules:
3-of-a-kind with different suits
4-card-run (with incrementing values) with same suits
Basically... I need to check each card's value and suits... and count the duplicates and uniques of values and suits. I'm struggling passing in an array of objects and using reduce on it.
Output: something like
melds : {
values: [4, 4, 4]
suits: [spades, hearts, diamonds]
},
runs : {
values: [11, 12, 13],
suits: ['spades', 'spades', 'spades']
}
Code:
function calculate(cards) {
var my_array = cards.reduce(function(prev_array, curr, index, array){
if (prev_array.duplicates.values.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.duplicates.values.push(curr);
} else {
prev_array.uniques.values.push(curr);
}
if (prev_array.duplicates.suits.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.uniques.suits.push(curr);
} else {
prev_array.duplicates.suits.push(curr);
}
return prev_array;
},
{
duplicates : {
values : [],
suits : []
},
uniques : {
values : [],
suits : []
}
}
);
return my_array;
}
Edit:
var values = [2, 3, 4, 5, 6, 6, 6];
var suits = ['spades', 'spades', 'spades', 'spades', 'diamonds', 'clubs', 'hearts'];
var test_values = potentialRunsAndMelds(values);
var test_suits = potentialRunsAndMelds(suits);
function potentialRunsAndMelds(array) {
var my_array = array.reduce(function(prev_array, curr, index, array){
if (prev_array.duplicates.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) {
prev_array.duplicates.push(curr);
} else {
prev_array.uniques.push(curr);
}
return prev_array;
},
{
uniques : [],
duplicates : []
}
);
return my_array;
}
var values = [2, 3, 4, 5, 6, 6, 6];
var suits = ['spades', 'hearts', 'spades', 'spades', 'diamonds', 'clubs', 'spades'];
EDIT 2:
var runs = Object.keys(groups.suits).map(function (suit) {
var values = groups.suits[suit].sort();
var run = [];
for (var i = 0; i < values.length; i++) {
if (values[i+1] - values[i] === 1) {
if (run.indexOf(values[i+1]) === -1) {
run.push(values[i+1]);
}
if (run.indexOf(values[i]) === -1) {
run.push(values[i]);
}
}
}
if (run.length >= 4) return run;
});
Returns : [Array[4], undefined, undefined, undefined]
Where Array[4] is [2, 3, 4, 5]
How can I not return undefined?
I suppose I can just do:
runs = runs.filter(function (run) {
return run.length;
});
You might be trying to do too much in one reduce function; maybe try breaking this into steps? Anyway, having your reducer group by value and suit would simplify things.
var groups = cards.reduce(function (accumulator, card) {
// Group by value.
accumulator.values[card.value] = accumulator.values[card.value] || [];
accumulator.values[card.value].push(card.suit);
// Group by suit.
accumulator.suits[card.suit] = accumulator.suits[card.suit] || [];
accumulator.suits[card.suit].push(card.value);
return accumulator;
}, {values: {}, suits: {}});
Once you've done that, it's much easier to find melds and runs.
// Melds
var meldValues = Object.keys(groups.values).filter(function (value) {
// Check for duplicates in this array, if so inclined.
return groups.values[value].length >= 3;
});
// Runs
var runs = Object.keys(groups.suits).map(function (suit) {
var values = groups.suits[suit].sort();
// (1) iterate over values
// (2) append each value to current 'run' as long as it's consecutive
// (3) if not consecutive, start a new run. if duplicate, discard.
// (4) return all runs with length >= 4
});
OK, I don't gamble and maybe I quite don't understood what you want to achieve, but it seem to me, that you forgot to get property value from cards array.
You are passing the curr
if (prev_array.duplicates.values.indexOf(curr) !== -1 ...
But curr is card object.
cards = [
{
suit: 'spades',
value : 4
},
You should target suit and value like curr.suit and cur.value.
And because in JS, Objects cannot be easily compared so lastIndexOf(object) === indexOf(object) equals in all cases. and you need to check uniques Array if the value is'nt already there, because you cannot rely on lastIndexOf().
Also, because Objects cannot be easily compared, testing duplicity with Object is bad idea.
I'm trying to create a JSON file out of this array named table
table is a two dimensional array, and its second level contains:
[name, id, parent]
and I'd like to transform them into a JSON, but I don't know if I'm in the right direction or if there's a better way to do it. Can you help me?
Thanks in advance.
My Code:
var table = [
["name1", 1, 2],
["name2", 2, 3],
["name3", 3, 0],
["name4", 4, 1],
["name5", 5, 3]
];
function deepcheck(dad) {
for (var i = 0; i < table.length; i++) {
if (table[i][2] === dad) {
console.log('{' + table[i][1] + '}');
var h = table[i][1];
deepcheck(h);
}
}
}
for (var i = 0; i < table.length; i++) {
if (table[i][2] === 0) {
console.log('[{');
console.log(table[i][0] + ',' + table[i][1] + '[');
var t = table[i][1];
deepcheck(t);
}
}
Maybe this fits your need.
For a JSON string just use JSON.stringify(obj).
This solution heavily features the Array.prototype.reduce() method.
function getChildren(parent) {
// Array.reduce is a method which returns a value. the callback can have up to
// 4 parameters, a start value `r`, if defined, otherwise the first element of the
// array, the array element (maybe it starts with the second) `a`, the index (not
// defined here) and the object itself (not defined here).
// to make a structure i need to iterate over the given data `table` and look
// for a given parent. if found then i have to look for their children and iterate
// over the `table` again, until no children is found.
return table.reduce(function (r, a) {
// test if the parent is found
if (a[2] === parent) {
// if so, generate a new object with the elements of `cols` as properties
// and the values of the actual array `a`
// like { name: "name3", id: 3, parent: 0 }
var row = cols.reduce(function (rr, b, i) {
rr[b] = a[i];
return rr;
}, {});
// create a new property `children`and assign children with the actual id
// as parentId
row['children'] = getChildren(a[1]);
// push row to the result
r.push(row);
}
// return the result
return r;
// start value for r is an empty array
}, []);
}
var table = [
["name1", 1, 2],
["name2", 2, 3],
["name3", 3, 0],
["name4", 4, 1],
["name5", 5, 3]
],
cols = ['name', 'id', 'parent'],
obj = getChildren(0);
document.write('<pre>' + JSON.stringify(obj, null, 4) + '</pre>');
I want to 'reduce' the array to only max values for each x (or index 0) value in a JavaScript multidimensional array.
My Array is as follows:
var mulitple = [["1/2013", 1],
["1/2013", 5],
["1/2013", 7],
["1/2013", 6],
["1/2013", 5],
["2/2013", 7],
["2/2013", 10],
["2/2013", 10],
["3/2013", 7],
["3/2013", 10],
["3/2013", 10],
["4/2013", 1],
["4/2013", 5],
["4/2013", 7],
["4/2013", 6],
["4/2013", 5],
["5/2013", 7]];
So the final result should be as follows:
[["1/2013", 7],
["2/2013", 10],
["3/2013", 10],
["4/2013", 7],
["5/2013", 7]];
How can I achieve this in JavaScript.
EDIT:
Aww man who voted my question down.
Anyhow, this is what I have come up with.
var max = 0;
var newarray = [];
for (var i = 1; i < mulitple.length; i++) {
if (mulitple[i - 1][0] == mulitple[i][0]) {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
}
else {
if (mulitple[i - 1][1] > max) {
max = mulitple[i - 1][1];
}
newarray.push([mulitple[i - 1][0], max]);
max = 0;
}
}
newarray.push([mulitple[mulitple.length - 1][0], max]);
The problem that I am having is that I can't get that last value (for the lone record) to get in the array. This was my result after I ran the code above.
[["1/2013", 7], ["2/2013", 10], ["3/2013", 10], ["4/2013", 7], ["5/2013", 0]]
This assumes that original array is already sorted. If not, you will have to write additional function to sort out.
function findMaximums(data) {
var out = [], maximums = {}, order = new Set;
data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][1] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
acc[pair[0]] = pair; // Store maximum in accumulator
order.add(pair[0]) // Store order in set
}
return acc;
}, maximums);
order.forEach(function(key) {
out.push(maximums[key]); // Populate out with maximums by order
});
return out;
}
findMaximums(multiple);
/*[
[
"1/2013",
7
],
[
"2/2013",
10
],
[
"3/2013",
10
],
[
"4/2013",
7
],
[
"5/2013",
7
]
]*/
Update 1: same, but without Set.
function findMaximums(data) {
var order = [];
var maximums = data.reduce(function(acc, pair) {
if (
// Accumulator has value and it's lower than current
(acc[pair[0]] && acc[pair[0]][2] < pair[1]) ||
// Accumulator doesn't have value
!acc[pair[0]]
) {
// Store maximum
acc[pair[0]] = pair;
// Store order
if (order.indexOf(pair[0]) === -1) {
order.push(pair[0])
}
}
return acc;
}, {});
return order.map(function(key) {
return maximums[key]; // Populate out with maximums by order
});
}
Update 2: Shorter version.
function findMaximums(data) {
return data.filter(function(p1, i1) {
return !data.some(function(p2, i2) {
return p1[0] === p2[0] && ( (p1[1] < p2[1]) || (p1[1] === p2[1] && i1 > i2) );
});
});
}
In this version I let pair to remain in output if there are no other pairs in input data that:
Have same month.
Have bigger value.
or
Have same value, but occur earlier than tested pair. This prevents duplicates.
Read here more about used Array methods: filter, some.
Assuming the array as defined in the original question, which is sorted to have each grouping together...
Completely untested code:
var reduced = [];
var groupName = '';
var groupMax;
var groupIndex;
var l = multiple.length; // Grab the array length only once
for (var i = 0; i < l; i++){
// Current Group Name doesn't match last Group Name
if (multiple[i][0] !== groupName) {
// Last Group Name is not empty (it's not the first Group)
if (groupName !== '') {
// Assume groupIndex has been set and grab the item
reduced.push(multiple[groupIndex]);
}
// Grab the new Group Name and set the initial Max and Index
groupName = multiple[i][0];
groupMax = multiple[i][1];
groupIndex = i;
}
// Current Value is bigger than last captured Group Max
if (multiple[i][1] > groupMax) {
// Grab the new Group Max and the current Index
groupMax = multiple[i][1];
groupIndex = i;
}
}
// Grab the last index, since there's no new group after the last item
reduced.push(multiple[groupIndex]);
There could be some syntax or logic errors. I didn't actually run this code, but I think the concept is correct.
Here's a tested version using a map to collect all the unique values, then the output is sorted by month/year and is independent of the order of the input data. This also works in all browsers (IE6+).
Working demo: http://jsfiddle.net/jfriend00/dk1tc73s/
function findLargest(list) {
var map = {}, output = [], item, key, val, current;
for (var i = 0; i < list.length; i++) {
item = list[i];
key = item[0];
val = item[1];
current = map[key];
if (current) {
// this date is in the map, so make sure to save the largest
// value for this date
if (val > current) {
map[key] = val;
}
} else {
// this date is not yet in the map, so add it
map[key] = val;
}
}
// map contains all the largest values, output it to an array
// the map is not in a guaranteed order
for (var key in map) {
output.push([key, map[key]])
}
// actually parse to numbers in sort so the comparison
// works properly on number strings of different lengths
function parseDate(str) {
var split = str.split("/");
return {m: +split[0], y: +split[1]};
}
// now sort the output
output.sort(function(t1, t2) {
var diffYear, a, b;
a = parseDate(t1[0]);
b = parseDate(t2[0]);
diffYear = a.y - b.y;
if (diffYear !== 0) {
return diffYear;
} else {
return a.m - b.m;
}
});
return output;
}