Use child array to get count for table - javascript

I have some json data. I need to count the number of strings in one of the children then create a table that is grouped by part of the child strings and used the count of specific child strings in the table. Confusing and impossible as it seems, this is what we need.
Honestly, I am barely sure where to even start here. Just displaying the strings correctly was a nightmare.
Here's some example json, I'm using for in loops to get through the levels of json above this, that part works fine:
"DataValues": [
{
"Key": "Stuff Type",
"Id": "95492",
"ComboBoxPairs": [
{
"Value": {
"Key": "3 Gallon",
"Value": "3 Gallon",
"ExtraValues": []
},
"Children": [
{
"Key": "Scan",
"Id": "93478",
"Strings": [
"DogType:Lab,Age:3,Name:Bowser",
"DogType:Lab,Age:5,Name:Dingo",
"DogType:Mutt,Age:1,Name:Muttman",
"DogType:Weiner,Age:1,Name:Goof",
"DogType:Mutt,Age:5,Name:Muttman",
"DogType:Puppy,Age:1,Name:Silly",
"DogType:Puppy,Age:1,Name:Sammy",
"DogType:Puppy,Age:1,Name:Shooter",
"DogType:Puppy,Age:1,Name:Doc",
]
}
]
},
{
"Value": {
"Key": "1 Gallon",
"Value": "1 Gallon",
"ExtraValues": []
},
"Children": [
{
"Key": "Scan",
"Id": "93478",
"Strings": [
"DogType:Puppy,Age:1,Name:Wingo",
"DogType:Puppy,Age:1,Name:Scrappy",
"DogType:Mutt,Age:4,Name:Goober"
]
}
]
}
]
}
]
Here's what I need to build:
DogType ContainerType Quantity Volume
Lab
3 Gallon 2 6 Gallon
Mutt
1 Gallon 1 1 Gallon
3 Gallon 2 6 Gallon
Weiner
3 Gallon 1 3 Gallon
Puppy
1 Gallon 2 6 Gallon
3 Gallon 4 12 Gallon
I am honestly not even sure where to get started
Honestly, I'm not even sure if this is possible? You can see that the table needs to be grouped by part of the string, the DogType. I then need to be able to count how many strings with a certain dog type there are in each ContainerType object, and pass that into the Quantity and ContainerType columns in the table. Then volume is multiplying the Gallon value, which is just text, by the quantity.
I'm sure I need to put more code but I can't even think of how to manage this. There could also be multiple ContainerTypes, this data isn't static.
The data isn't my design but I can't change it, just getting to this point has been a disaster. Can anyone think of a way to do this?

Okay so what you need to do is transform some data. Basically change the way a certain data is stored / represented. Now the Strings are in a consistent format, so we can use a combination of string operations to get a key => value store out of it (I am going to use ES6 syntax to simplify boilerplate)
let parseString = str => {
let pairs = str.split(',');
let obj = {};
pairs.forEach(pair => {
pair = pair.split(':');
obj[pair[0]] = pair[1];
});
return obj;
};
This function simply takes one of your strings, "DogType:Lab,Age:3,Name:Bowser" for example, and splits out an object such as {'DogType': 'Lab', 'Age': 3...}. Now that we have this, we can start to manipulate and group the data itself (if you're not sure what array.map does, it basically gives a new array after running a function on each of the array's values).
let comboBoxPairs = data.ComboBoxPairs.map(comboBoxPair => {
comboBoxPair.Children = comboBoxPair.Children.map(children => parseString(children));
return comboBoxPair;
});
Now we have replaced the incomprehensible array of strings into an array of objects we can start to group. First we need to figure out a data structure. You're grouping by dog type first, so our object's keys are the Dog's Type. Then we simply need to add each quantity into the dog's type and increment a count each time the quantity is encountered.
let dogType = {};
comboBoxPairs.forEach(comboBoxPair => {
comboBoxPair.Children.forEach(children => {
if (typeof dogType[children.DogType] === 'undefined') {
dogType[children.DogType] = {};
}
if (typeof dogType[children.DogType][comboBoxPair.Value.Key] === 'undefined') {
dogType[children.DogType][comboBoxPair.Value.Key] = 0;
}
dogType[children.DogType][comboBoxPair.Value.Key]++;
});
});
And you're done! This gives you a Object such as :
{
'Lab': {
'1 Gallon': 2,
....
},
};
You can then loop through this object to display the values and totals.

Is your table example illustrative or is it meant to represent your sample data?
Your table does not seem to tally with the sample data so I am in doubt as to whether my understanding is correct?
If I can start with a generalised answer - which may not be enough but it may head you in the right direction~
1. If you can parse the data in full before seeking to output your results then you can build a summary array which makes your 'un-ordered' input data trivial - then presentation becomes a separate step.
2. If you have to do it in one pass - then you would normally think about sorting the data first - on the input - this is so you can handle mutt's appearing at non-contiguous points.
Make sense?
My personal preference would be to build your running totals into a separate data structure - whether you have scenario 1 or 2 so that you can trap the increment no matter what level you find your relevant keys at within the for-in.
In short - is there any conceptual problem with building a new object for your summary?

Related

How to Extract data based on the values in one array after matching the corresponding values from another array in JavaScript?

This is the URL from GeoServer to get feature info
{"type":"FeatureCollection","features":[{"type":"Feature","id":"weather_warning_day_1.fid--418ec0da_178b69d5dfc_-715c","geometry":null,"properties":{"issue_date":"2021-04-09","updated_at":"2021-04-09T09:26:33+05:30","utc_time":0,"state_name":"Odisha","state_id":21,"district_name":"MAYURBHANJ","district_id":232,"api_district_name":"MAYURBHANJ","day_1":"6,9,10","day1_color":3}}],"totalFeatures":"unknown","numberReturned":1,"timeStamp":"2021-04-09T15:38:19.536Z","crs":null}
the data I want to extract is of variable: "day_1":"6,9,10"
which I got from the layer and stored it in the variable as
var warning_day_1 = weather_warning_layer_data.features[0].properties.day_1
so basically the input is "day_1":"6,9,10"
which I have stored in the array as
[{"warning":"6"},{"warning":"9"},{"warning":"10"}]
and corresponding output should be Dust Storm, Heat Wave, Hot Day
Dust Storm, Heat Wave, Hot Day
or if the input was "day_1":"2,5"
then output should have been Heavy Rain, Hailstorm
or if the input was "day_1":"1"
then output should have been No Warning
After reading the data of the string and creating its array, I have to compare it with another array and extract the key values (display) corresponding to the key values (warning) in the 1st array.
var warning_data_split = warning_day_1.split(/[ ,]+/);
var warning_data_from_api_array = new Array;
warning_data_from_api_array.push(warning_data_split);
for (var i = 0; i < warning_data_from_api_array.length; i++) {
var item_in_array_to_compare = warning_data_from_api_array[i];
if(warning_data_from_api_array[item_in_array_to_compare.warning_data_from_api_array])
{warning_data_from_api_array[item_in_array_to_compare.warning_data_from_api_array].push(item_in_array_to_compare);}
else {
warning_data_from_api_array[item_in_array_to_compare.warning_data_from_api_array] = [item_in_array_to_compare];}}
let final_array_to_compare = item_in_array_to_compare
final_array_to_compare = final_array_to_compare.map(x => ({warning: x}));
/// this is the first array ////////////
The values in this array are not static in length, as it keeps on changing like, sometimes the array has value [1] or [1,2], [2,5,8], [4,7,12], etc
so I have to extract the corresponding values of display from the lookup array given below
var warning_code_meaning_list = [
{ warning:"1", display:"No Warning"},
{ warning:"2", display:"Heavy Rain"},
{ warning:"3", display:"Heavy Snow"},
{ warning:"4", display:"Thunderstorm & Lightning, Squall etc"},
{ warning:"5", display:"Hailstorm"},
{ warning:"6", display:"Dust Storm"},
{ warning:"7", display:"Dust Raising Winds"},
{ warning:"8", display:"Strong Surface Winds"},
{ warning:"9", display:"Heat Wave"},
{ warning:"10", display:"Hot Day"},
{ warning:"11", display:"Warm Night"},
{ warning:"12", display:"Cold Wave"},
{ warning:"13", display:"Cold Day"},
{ warning:"14", display:"Ground Frost"},
{ warning:"15", display:"Fog"}
]
The data which I am getting in warning_day_1 (in the very first line of the code) is a string (this couldn’t be saved as float/integer in the database column because sometimes there are more than 1 warning for a specific place, so I have stored this as a text in the database)
Which I’m converting to an array after reading it from the API
Now this string, which I am fetching from API has variable data,
Some time single digit like: 1
Sometime multiple : 1,2,3
And each of the integer present in this array corresponds to the specific text shown in the next array like if the warning is 2 it means the heavy rainfall,
but if the string (later converted to an array, with “warning” as a key) has 2,5 as value, it means: heavy rainfall & Hailstorm
I want that the values which come up in array 1 (the dynamic one) got match with the 2nd array ( a sort of lookup array) and fetch its display value as output.
How to do so?
You could use an object to map your warnings to messages.
Try this:
const data = {"type":"FeatureCollection","features":[{"type":"Feature","id":"weather_warning_day_1.fid--418ec0da_178b69d5dfc_-715c","geometry":null,"properties":{"issue_date":"2021-04-09","updated_at":"2021-04-09T09:26:33+05:30","utc_time":0,"state_name":"Odisha","state_id":21,"district_name":"MAYURBHANJ","district_id":232,"api_district_name":"MAYURBHANJ","day_1":"6,9,10","day1_color":3}}],"totalFeatures":"unknown","numberReturned":1,"timeStamp":"2021-04-09T15:38:19.536Z","crs":null}
var warning_code_meaning_list = {
"1":"No Warning",
"2":"Heavy Rain",
"3":"Heavy Snow",
"4":"Thunderstorm & Lightning, Squall etc",
"5":"Hailstorm",
"6":"Dust Storm",
"7":"Dust Raising Winds",
"8":"Strong Surface Winds",
"9":"Heat Wave",
"10":"Hot Day",
"11":"Warm Night",
"12":"Cold Wave",
"13":"Cold Day",
"14":"Ground Frost",
"15":"Fog",
};
results = data["features"].map(feature => {
return feature.properties.day_1.split(',').map(code => {
return warning_code_meaning_list[code];
});
});
That gives you an array of arrays of the displays:
[ [ 'Dust Storm', 'Heat Wave', 'Hot Day' ] ]

Get "leaderboard" of list of numbers

I am trying to get a kind of "leaderboard" from a list of numbers. I was thinking of making an array with all the numbers like this
var array = [];
for (a = 0; a < Object.keys(wallets.data).length; a++) { //var wallets = a JSON (parsed) response code from an API.
if (wallets.data[a].balance.amount > 0) {
array.push(wallets.data[a].balance.amount)
}
}
//Add some magic code here that sorts the array into descending numbers
This is a great option, however I need some other values to come with the numbers (one string). That's why I figured JSON would be a better option than an array.
I just have no idea how I would implement this.
I would like to get a json like this:
[
[
"ETH":
{
"balance":315
}
],
[
"BTC":
{
"balance":654
}
],
[
"LTC":
{
"balance":20
}
]
]
And then afterwards being able to call them sorted descending by balance something like this:
var jsonarray[0].balance = Highest number (654)
var jsonarray[1].balance = Second highest number (315)
var jsonarray[2].balance = Third highest number (20)
If any of you could help me out or point me in the right direction I would appreciate it greatly.
PS: I need this to happen in RAW JS without any html or libraries.
You should sort the objects before making them a JSON. You can write your own function or use a lambda. See this [https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value]
Since you are dealing with cryptocurrency you can use the currency-code as a unique identifier.
Instead of an array, you can define an object with the currency as properties like this:
const coins = {
ETH: [300, 200, 500],
BTC: [20000, 15000, 17000]
}
then you can access each one and use Math.max or Math.min to grab the highest / lowest value of that hashmap. E.G. Math.max(coins.BTC)
And if you need to iterate over the coins you have Object.keys:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Thank you all for your answer. I ended up using something like:
leaderboard = []
for (a = 0; a < Object.keys(wallets.data).length; a++) {
if (wallets.data[a].balance.amount > 0) {
leaderboard.push({"currency":wallets.data[a].balance.currency, "price":accprice}) //accprice = variable which contains the value of the userhold coins of the current coin in EUR
}
}
console.log(leaderboard.sort(sort_by('price', true, parseInt)));

Javascript loop works the first time ran, but gives NaN the second time without logic

I'm developing a javascript game and am altering values from a JSON file through a loop. The loop however occasionally replaces the values it should alter with "NaN" and grabs a random letter from the prefix word of the array. I tried debugging the values and putting in fixed creature values, but that has made me none the wiser.
The code works THE FIRST TIME RAN in JSFiddle: https://jsfiddle.net/ezwad5mL/2/ but whenever you run it a second time, it overwrites the values in the loop with NaN and the letter. I think it's because the function random_int needs 2 values but it only inputs 1 the second time you run it, which is somehow the same value from the previous input (which it altered in the second For loop). What I don't understand is how this code doesn't reset the storedDungeon if it fires the second time.
I understand the problem I think, but I have no clue what's wrong with what I wrote and why it does okay the first time, but screws up the second time.
function random_item(items){
return items[Math.floor(Math.random()*items.length)];
}
function random_int(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
var storedDungeon = []
const jsonCreatures = {
"easy": [
{ "name": "Scorchbird", "hp": [6,13], "prefix": ["Weak", "Small", "Young", "Wild"],
"damage": [1,5], "droprateCommon": [0,60], "droprateRare": [70, 90]},
{ "name": "Reanimated Corpse", "hp": [8,15], "prefix": ["Weak", "Festering"], "damage":
[3,5], "droprateCommon": [0,40], "droprateRare": [50, 80]}
]}
var randNumber = 2
for (let i = 0; i < randNumber; i++) {
let randomObject = random_item(jsonCreatures.easy)
storedDungeon.push(randomObject)
}
for (let o = 0; o < storedDungeon.length; o++) {
storedDungeon[o].hp = random_int(storedDungeon[o].hp[0], storedDungeon[o].hp[1])
storedDungeon[o].damage = random_int(storedDungeon[o].damage[0],storedDungeon[o].damage[1])
storedDungeon[o].prefix = random_item(storedDungeon[o].prefix)
}
console.log(storedDungeon)
To understand the problem we need to understand how arrays work.
The following example may open your eyes to the problem.
const creatures = [
{
name: 'Scorchbird'
}
]
const dungeon = []
dungeon.push(creatures[0])
dungeon[0].name = 'Reference to the object!'
console.log(creatures)
// [
// {
// name: 'Reference to the object!'
// }
// ]
When we add a creature (object) to our dungeon array
dungeon.push(creatures[0])
we actually add a reference to the original object, not a copy of it.
This means that when you do
storedDungeon[o].hp = random_int(
storedDungeon[o].hp[0],
storedDungeon[o].hp[1]
)
you alter the original creatures objects and their properties.
In this case the array hp: [6, 13] is replaced with a random (single!) number, for example hp: 8
And when your code runs the second time there is now hp array to do hp[0] and hp[1], just a single number. This is why the function random_int returns NaN (not a number).
The same effect happens with the damage and with the prefix. However because prefix is a string the random_item function will return a random character in this string. That's because the characters of a string can be accessed by their index like in an array: "im a string"[1] = "m"
I think Nico Gräf's explanation is correct. To fix this, you can create a clone of the object and have it pushed to the storedDungeon.
storedDungeon.push({...randomObject})
Please note that spread syntax doesn't deep clone your object. So it only works to the first level of the object, which should be fine with your current design.

How to implement bi-directional connection between two arrays?

In my app I have an ItemsService which gets Items from Server and stores them as JSON objects in its cache variable. Items can be present in many places, e.g. in table or in graph/chart and so on.
For example, when I initialize a table - I need to pick only specific Items from cache, e.g. 1st, 3rd, 7th.
How to implement bi-directional connection between them? Basically I want table to contain references to specific items from cache so when I change an Item either in cache or in table - its state will be in sync all the time because it's the same Item.
Also when I delete Item from table - it needs to be removed from cache.
Here's example of table and cache structures:
Table:
table: {
"36": { // it's a name of a row
"72": [items], // it's a name of a column with corresponding items
"73": [items],
"74": [items]
},
"37": {
"72": [],
"73": [items],
"74": [items]
},
"38": {
"72": [],
"73": [],
"74": []
}
}
ItemsService cache (simplified version):
ItemsService = {
cache: [items]
};
Item structure:
{
id: 3,
parent_id: 1,
name: 'First Item',
siblings: [1,2,3],
active_users: [{user_id: 1, avatar_url: 'http://...'}, ...],
// 50 more fields :)
}
Also need to point out that I use angular-ui-sortable plugin to allow dragging of Items between columns/rows and I need to provide ng-model with array(I think). Here's how it looks right now:
<td ui-sortable="vm.sortableOptions"
ng-model="vm.table[row.id][column.id]">
<sb-item itemid={{item.id}}
ng-repeat="item in vm.table[row.id][column.id]">
</sb-item>
</td>
Unless for some reason you have to use two separate arrays, have you considered using a filter?
Your best bet would be objects. Variables that hold objects in javascript aren't really holding the object but a reference to said object. When you pass that variable into another one, the reference value gets copied so both variables point toward the same object.
var a = { 0: 'Property 0' };
var b = a;
b[0] = 'Property 0!'; //a[0] also the same.
delete b[0]; //a[0] nor b[0] exist anymore.
Using objects (rather than JSON) would work.
Then your cache and your table both point to same item objects. If you change something in the object, it is reflected at both ends.
var cacheArray = [{ item: 1 }, { item: 2}];
table[0][0] = cacheArray[0];
console.log(table[0][0].item); // 1
cacheArray[0].item = 9;
console.log(table[0][0].item); // 9.
Please note that the array and the table have not changed. They still point to the same objects.

One array or many? (hash table)

I've an array that is being used to store the conversion factors for a conversion program I'm currently working on.
A short Example:
var Length =
{
"lengthsA" :
{
"inch" : 0.0254,
"yard" : 0.9144,
"mile" : 1609.344,
"foot" : 0.3048,
"metres": 1
}}
This will become much bigger and there are many more of them.
It seems I have two options. I can either declare many arrays, one for each conversion type and in the function use and if else to dictate which one should be called upon for the conversion. The alternative is to use one huge array that stores everything. This would nullify the need for an if else and also remove the need to declare many arrays but at the cost of combining everything into what could become one big mess.
I'm in favour of the first option, mainly because I like modularity and it'd be easier for debugging / editing.
I'm also concerned about speed and access time. With one large array would there be an impact seeing as I'm using keys to determine what values are called. Key above would be "lengthsA"
Thanks.
If I were doing this project, I'd definitely use a hierarchical structure. I might start with something like this:
var conversions = {
length : {
lengthsA : {
inch : 0.0254,
yard : 0.9144,
mile : 1609.344,
foot : 0.3048,
metres: 1
},
lengthsB : {
. . .
}
},
mass : {
},
. . .
}
The structure is: conversions.<category>.<conversion_group>.<unit_name>. It's probably as easy to maintain as any other structure.
You might consider adding a property reference that would indicate the name of the unit that should be the reference (e.g., reference : "metres" in the case of lengthsA). I'd also be more consistent about unit names ("inch" is singular; "metres" is plural). Depending on your application, you might also want to have each conversion be a structure with a value and an uncertainty. (Some conversion factors are exact; others are not.)
Hard to say without knowing all the details of your program, but I wouldn't use hierarchical objects for storing units, but rather a flat array, similar to a SQL table:
units = [
{ category: "length", name: "inch" , value: 0.0254 },
{ category: "length", name: "yard" , value: 0.9144 },
{ category: "length", name: "mile" , value: 1609.344 },
{ category: "length", name: "foot" , value: 0.3048 },
{ category: "length", name: "meter", value: 1 }
]
You will need a couple of utility functions to find items in this table (like getUnitsByCategory), but once you've got it, you'll find this structure much easier to work with. Uniformity is the king!
if you define variable for javascript so..
var inch=0.0254,
yard=0.9144
youcan write
<option>inch</option>
and acces it with
window[document.select.textContent]
it's much faster but the code would be much longer.
In your case the readability is more important
so yes create a multidiminsional object.(groups)
it's also easier to access the values.
obj={
"length":{
inches:0.0254,
miles:1609.344,
},
"weight":{
kg:1
}
}
so you can access it by
obj.length.inches
or
obj['length']['inches']
and write
window.onload=function(){
var obj={
length:{
inches:0.0254,
miles:1609.344,
}
}
var select1=document.createElement('select'),
select2=null,
f=document.createDocumentFragment(),
input=document.createElement('input'),
convert=document.createElement('button');
for(var a in obj.length){
f.appendChild(document.createElement('option')).textContent=a;// easyway to access
}
select1.appendChild(f);
select2=select1.cloneNode(true);
input.type='text';
convert.textContent='Convert';
convert.addEventListener('click',function(e){
console.log(
input.value,
obj.length[select1.textContent],// easyway to access
obj.length[select2.textContent]// easyway to access
)
},false);
var bdy=document.body
bdy.appendChild(input);
bdy.appendChild(select1);
bdy.appendChild(select2);
bdy.appendChild(convert);
}

Categories

Resources