Group to Category Conversion
var Auto = ['new_auto', 'add_auto'];
//more category objects
var categories = [Auto, Fire, Health, Life, Bank];
function groupToCat(group) {
for (x = 1; x < categories.length; x++) {
for (i = 1; i < categories[x].length) {
if (group == categories[x][i]) {
return categories[x]
}
}
}
}
I'm trying to use a loop within a loop in combination with a multi-dimensional array to achieve a neat conversion function from a group (string new_auto) to a string equal to the name of the category containing it (array object Auto).
But as it is, I'm returning the category object, not its name. How can I do this?
Dynamic item "de-categorization" function
As suggested by Palmsey in the comments, this is the proper way of discovering an object's parent category through the combination of a loop-within-a-loop and a multidimensional array.
//Define Category Names and Children
var Auto = {
name: 'Auto',
items: ['new_auto', 'add_auto']
};
var Fire = {
name: 'Fire',
items: ['new_fire', 'add_fire']
};
var Health = {
name: 'Health',
items: ['health']
};
//Bring each category into a single array
var categories = [Auto, Fire, Health, Life, Bank];
//Because of the complimentary nature of loops and arrays, we can
//access each additional dimension of the array with an additional
//loop dimension.
function groupToCat(group) {
for (x = 0; x < categories.length; x++) {
for (i = 0; i < categories[x].items.length; i++) {
if (group == categories[x].items[i]) {
return (categories[x].name);
}
}
}
return ("No match found!");
}
I chose this method above the alternatives because it's dynamic. Simply by continuing the pattern, we can convert the values of items to the groups that they belong to, and vice-versa, with any level of group complexity.
Related
I'm building a dc.js / d3.js dashboard in which I often have to make crossfilter groups containing a quantity for each key and a percentage of the total value for each key. That is why I want to make generic reduceAdd, reduceRemove and reduceInitial functions. I managed doing the first 2, but I don't understand reduceInitial behaviour :
function reduceAdd(dim,grouping,col) {
var keys = getKeys(dim); // get the keys name in an array of string
return function(p,v) {
p.total += parseInt(v[col]); // get the running total
for (var i = 0; i < keys.length; i++) {
if(v[grouping] == keys[i]) {p[keys[i]] += +v[col];}
p[keys[i]+"perc"] = p[keys[i]]/p.total; // calculate a percentage for ech key
}
return p;
}
}
function reduceRemove(dim,grouping,col) {
var keys = getKeys(dim);
return function(p,v) {
p.total -= parseInt(v[col]);
for (var i = 0; i < keys.length; i++) {
if(v[grouping] == keys[i]) {p[keys[i]] -= +v[col];}
p[keys[i]+"perc"] = p[keys[i]]/p.total;
}
return p;
}
}
This is the working non generic function reduceInitFC() {
return {total:0, LILOU:0, MARIUS:0,ORIANE:0,LILOUperc:0,MARIUSperc:0,ORIANEperc:0};
}
This is what I tried :
function reduceInit(dim) {
var keys = getKeys(dim);
var initArray= {};
initArray["total"] = 0;
for (var i = 0; i < keys.length; i++) {
initArray[keys[i]] = 0;
initArray[keys[i]+"perc"] = 0;
}
console.log(initArray); // (1)
console.log({total:0, LILOU:0, MARIUS:0,ORIANE:0,LILOUperc:0,MARIUSperc:0,ORIANEperc:0});
return function() {
return initArray;
}
}
The result is :
(1) The output gives 0 for all the keys every two iterations and some non zero values for the other iterations
When I use this function the resulting values in the group are constant respect with the keys what is not the case in reality and not the case when I hand write the zero values.
If anyone can help, it would be super kind and useful.
Best,
Theo
I think your reduceInit looks fine, but I don't think this is possible through the reduce. The reduce calculates totals incrementally -- and don't make sense until the group runs through all the rows in the dimension. So the percentages for each key can't be calculated until reduce is finished. (Likewise, while the reduce can be used to calculate averages across a single key's values, they can't be used for averages across all keys because that would be dependent on the reduced total.) (UPDATED, also see this answer's comments)
But since you already have the reduced total and the total for the key / category, you can calculate the percentage in your chart's valueAccessor.
function valueAccessor(d){
return d.value[keyName] / d.value.total
}
var availableMarketGroups = {};
angular.forEach(function (market) {
if (availableMarketGroups[market.group_id]) { // market.group_id is not sorted id
availableMarketGroups[market.group_id].count++;
}
});
market.group_id - number ,not sorted, and sometimes its duplicates
availableMarketGroups[market.group_id].count - its length
Lets see the image for more info.
The numbers of the market groups don't represent real amount of markets.
availableMarketGroups[market.group_id].count show - 15 ,but in real it should be 5 (5 groups) ,because market.group_id is duplicates.
How can i ignore duplicated market.group_id values in if statement ?
var availableMarketGroups = {};
var groupsProcessed = [];
angular.forEach(availableMarketGroups, function(marketGroup) {
if (groupsProcessed.indexOf(marketGroup.group_id) < 0) {
groupsProcessed.push(marketGroup.group_id);
availableMarketGroups[market.group_id].count++;
}
});
Answer for counting unique array elements is to make a function as given below.
var counts = {};
for (var i = 0; i < arr.length; i++) {
counts[arr[i]] = 1 + (counts[arr[i]] || 0);
}
It will return the unique counts of elements in the array.
Reference : Count unique elements in array without sorting
Hard to say without your data. Generally speaking, you should be able to reduce this down to the unique Set of group_ids:
const uniqueGroups = markets.reduce(function(set, market) {
if (!set.has(market.group_id)) {
set.add(market.group_id);
}
return set;
}, new Set());
console.log('Unique group count: ', uniqueGroups.size);
You can use underscore:
var newObj = _.uniq(market, function(p){ return p.group_id; });
So I have a few arrays
var item = [{
name : "somename",
type1 : "wood",
location : "some location",
desc : "some description"
}, {
name : "somename",
type1 : "metal",
location : "somelocation",
desc : "some description"
}];
and
var shopState = 0;
var hasInv = [];
var pickedItem = [];
I assume I have 2 empty arrays with that last one. Later on I attempt to make use of these in a function.
function shop() {
for (var i = 0; i < item.length; i++) {
if (item[i].location == "some location") {
//get all items at some location
hasInv[i] = item[i];
}
}
if (shopState == 1) {
var d = 1;
for (var i = 1; i < hasInv.length; i++) {
if (hasInv[i].type1 == 'wood') {
//get all wood items at some location
pickedItem[d] = hasInv[i];
d++;
console.log(pickedItem);
}
}
}
}
That last bit with pickedItem returned undefined unless I declare pickedItem = []; in shopState when I thought I already declared it at the start of the file. It works when I do but I'm trying to understand why it does not if i don't.
A couple of questions: Is your conditional for shopState passing? I don't see where you increment shopState. Also is there a reason you are setting to indexes directly instead of using .push or .unshift? Maybe for time complexity concerns? I'm assuming the purpose of the function is to iterate through store locations to find matching locations and then find all items of a certain type at that location? Is that correct? If that is true here is the solution:
function shop() {
for (var i = 0; i < item.length; i++) {
if (item[i].location == "some location") {
//get all items at some location
hasInv.push(item[i]);
//You weren't increment shopState
shopState++;
}
}
if (shopState == 1) {
//conditional now passes
//there is no need to set a additional index variable.
for (var i = 0; i < hasInv.length; i++) {
if (hasInv[i].type1 == 'wood') {
//get all wood items at some location
pickedItem.push(hasInv[i]);
console.log(pickedItem);
}
}
}
}
shop()
Also note that your conditional makes it so you will only locate one item with the wood type. To change this so that your comment is true and you are collecting all items simply change your conditional to:
if(shopState >= 1)
As well you are only checking one location because you have combined the words some and location in the second object of your items array.
The function finds which tv character the user compares to based on their answers to my questions. My code now is very inefficient for multiple select menus!!! Maybe an object that takes all selectmenus in html and allows me to assign array values based on the selected index of a selectmenu.
function onSelectMenuBlur() {
"use strict";
/*list of arrays that will be added to when the user selects an option in a selectmenu.*/
var rickArray = [];
var shaneArray = [];
var bobArray = [];
var carolArray = [];
var lArray = [];
var sm = document.getElementById("selectmenu");
.onchange function that determines what array will be added to depending on the option selected in the select menu. This function will add an array value of 1 once to an array. Seems like an inefficient way, especially with multiple selectmenus!
sm.onchange = function() {
if(sm.selectedIndex + 1 === 1) {
rickArray.push(1);
shaneArray.pop();
bobArray.pop();
carolArray.pop();
lArray.pop();
alert(rickArray.length);
}
else if(sm.selectedIndex + 1 === 2) {
shaneArray.push(1);
rickArray.pop();
bobArray.pop();
carolArray.pop();
lArray.pop();
alert(shaneArray.length);
}
else if(sm.selectedIndex + 1 === 3) {
bobArray.push(1);
rickArray.pop();
shaneArray.pop();
carolArray.pop();
lArray.pop();
alert(bobArray.length);
}
else if(sm.selectedIndex + 1 === 4) {
carolArray.push(1);
rickArray.pop();
shaneArray.pop();
bobArray.pop();
lArray.pop();
alert(carolArray.length);
}
else if(sm.selectedIndex + 1 === 5) {
lArray.push(1);
rickArray.pop();
shaneArray.pop();
bobArray.pop();
carolArray.pop();
alert(lArray.length);
}
else{}
};
.onblur purpose to find array with biggest length or value out of all selectmenus to determine which person associated with the array the user is like. Again seems like an inefficient way to handle!
sm.onblur = function() {
var rickL = rickArray.length;
var shaneL = shaneArray.length;
var bobL = bobArray.length;
var carolL = carolArray.length;
var lL = lArray.length;
// unfinished if else statement !!
if(rickL > shaneL && rickL > bobL && rickL > carolL && rickL > lL) {
alert("you are Rick Grimes");
}
else{
alert("you are someone else");
}
};
}
Use a 2-dimensional array instead of separate arrays for each character, and then use the selected index as an index into the array.
var characters = [[], [], [], [], []];
sm.onchange = function() {
for (var i = 0; i < characters.length; i++) {
if (i == this.selectedIndex) {
characters[i].push(1);
alert(characters[i].length);
} else {
characters[i].pop();
}
}
};
To get the character names in there, make it an array of objects.
characters = [
{ name: "Rick",
array: []
},
{ name: "Carol",
array: []
},
...
}
Then you would use characters[i].array.push(1). And then when you want to say which character they are, find the object with the longest array and then print its .name.
I have a list of players in denoted as
activeRange[x]
where x will vary from day-to-day.
Each of the x values will have to have AT LEAST 4 more subsequent values (likely a bit more). Ideally I'd like the array to look like:
activeRange[x][y]
So here's what I've done so far:
var MATCH = AllData[TotalRows][TotalColumns+1];
activeRange[TotNumPlayers].push(MATCH);
This is all located within 3 nested for loops.
TotNumPlayers
will iterate through a given set declared at the beginning (somewhat like 23). Once done, the
TotalRows
will iterate, then finally
TotalColumns
I'm running into the following error:
TypeError: Cannot find function push in object mitch
mitch is the value of activeRange[0]. I've been staring at this way too long, so any help would be appreciated!
EDIT: Code inserted below:
PLEASE IGNORE ALL THE COMMENTS. I COPY/PASTED THIS FROM A BIT OF CODE I USED YESTERDAY TO PERFORM A DIFFERENT FUNCTION.
This is the second time I've ever posted on this website, so trying to format this monster to be pretty was scary sounding. Hopefully this is good enough.
This is how activeRange was declared and initialized.
var activeRange = new Array();
for (var b=0; b<=lastRow-2; b++){
activeRange[b] = sheetRANK.getRange(b+2,1).getValue();
}
This is the function.
function getTotalScore(activeRange, w) {
Logger.clear()
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetWAR = ss.getSheetByName('WAR');
var sheetRANK = ss.getSheetByName('RANK');
var AllData = sheetRANK.getDataRange().getValues();
Logger.log('First');
for (var TotNumPlayers = 0; TotNumPlayers <= activeRange.length; TotNumPlayers++) {
Logger.log('Second');
var f = 0;
for (var TotalColumns = 0; TotalColumns <= AllData[0].length; ++TotalColumns) { // Init n. If n <= the total columns (second dimension), inc n.
Logger.log('Third');
for (var TotalRows = 0; TotalRows <= AllData.length; ++TotalRows) { // Init i. If i <= the total rows (first dimension), inc i.
Logger.log('Fourth');
//try{ // to avoid errors.
if (activeRange[TotNumPlayers] != "") {
Logger.log('Here?');
if (AllData[TotalRows][TotalColumns].valueOf().toUpperCase() == activeRange[TotNumPlayers].toUpperCase()) {
Logger.log('How About Here?');
var MATCH = AllData[TotalRows][TotalColumns + 1];
activeRange.push(TotNumPlayers, MATCH);
for (var Calc = 0; Calc <= activeRange[TotNumPlayers].length - 1; Calc++) {
var OverallScore = ((activeRange[TotNumPlayers][0] * 1.0) + (activeRange[TotNumPlayers][1] * .75) + (activeRange[TotNumPlayers][2] * .50) + (activeRange[TotNumPlayers][3] * .25));
sheetRANK.getRange(activeRange[TotNumPlayers] + 1, 2).setValue(OverallScore);
f = f + 1;
}
if (TotalRows == AllData.length - 1 && TotalColumns == AllData[0].length - 1 && f == 0) {
Browser.msgBox('No names matching \'' + activeRange[TotNumPlayers] + '\' found. Check your spelling!');
return;
}
}
}
}
}
}
}
Try thinking about what kind of data structures you can use to make your life easier. For this particular case, you have a list of players that you want to associate some data with. You'd probably use a structure like:
activeRange = [
{
name: 'mitch',
data: []
}
]
When you want to update the data, you'd simply call activeRange[0].data.push(someData).
activeRange is an array of players and each player is represented by an object with some properties, (name, data, etc).
Calling activeRange[0] yields the first player in your array and activeRange[0].data will yield the data associated with that player, which you can then manipulate however you want (push, pop, etc)
Based on your comments, you need a structure more like this
var activeRange = [
{
name: 'mitch',
otherData: [
10,
11,
12,
13
]
},
{
name: 'viper',
otherData: [
//values
]
}
]
you can access that by activeRange[0].otherData[2]
to add to it, just push into the sub array activeRange[0].otherData.push(newValue)