RxJS to combine attributes from triples to a table - javascript

I have a service producing objects that are like triples. They will be in this format:
{ country, attribute, value }
Example:
{ country: 'usa', attribute: 'population', value: 100 }
{ country: 'mexico', attribute: 'population', value: 200 }
{ country: 'usa', attribute: 'areaInSqM', value: 3000 }
Ultimately I want to display these as a table. Rows are countries, columns are attributes. So the table would look like:
| country | population | areaInSqM |
| usa | 100 | 3000 |
| mexico | 200 | |
My assumption (possibly wrong) is that I need to create an intermediate data structure that is an array of rows. Such as:
[ { country: 'usa', population: 100, areaInSqM: 3000 }, .... ]
My current solution is a non-RxJS mess of objects where I store a Set containing each attribute type, store a lookup object indexed by country, and convert the lookup object back to the above array at the end. Lots of looping and double storage that I'd prefer to avoid.
Does RxJS have any operators that aid in this type of operation?
Is there a smarter approach?
In this particular case, assumptions are:
The attributes are not known ahead of time
The values are always numeric
A given 'cell' can be null. In this example, mexico areaInSqM is never provided
Edit: Plunkr with solution: https://plnkr.co/edit/FVoeVmmzMN7JGJ3zWFQM?p=preview

There are two components in your question, the data structure part, and the data flow part (I suppose you get these data as a stream i.e. one by one, hence the reason why you use Rxjs).
A simple way to iteratively build you data structure is to use the scan operator. For instance :
myDataStructure$ = dataSource$.scan(function (accDataStructure, triple){
accDataStructure[triple.country] = accDataStructure[triple.country] || {}
accDataStructure[triple.country][triple.attribute] = accDataStructure[triple.country][triple.attribute] || {}
accDataStructure[triple.country][triple.attribute] = triple.value
return accDataStructure
}, {})
That makes the assumption that dataSource$ produces objects of the shape { country, attribute, value }. Then myDataStructure$ will output, for every incoming data, the iteratively built data structure that you are seeking. If you only want that data structure once it is finished building, just add a .last() to myDataStructure$.
This is not tested so let me know if that worked

Related

Remove all keys from JSON object

Initially I was working with a CSV, where each row contained data e.g.
------------------------------------------------------
123 | cat | dog |
------------------------------------------------------
456 | cat | rabbit |
------------------------------------------------------
789 | snake | dog |
------------------------------------------------------
I am now getting more data with different structure, so I can no longer use a csv. Instead I am using JSON file. The JSON file looks something like this
[
[
{
"ID": 123,
"animal_one": "cat",
"animal_two": "dog"
},
{
"ID": 456,
"animal_one": "cat",
"animal_two": "rabbit"
},
{
"ID": 789,
"animal_one": "snake",
"animal_two": "dog"
}
],
[
2222
],
[
12345
],
[
"2012-01-02"
],
[
"2012-12-20"
]
]
So you can see the additional data. For this part of the application, I only want to work with the first part of the JSON, the part containing animals. The function I have basically works on values only, so I need to make this JSON like the original CSV file, whereby I only have the values, no keys.
So I am loading the JSON file, which is then contained within the variable data
I am then trying something like this
var key = "CUST_ID";
delete data[0][key];
console.log(JSON.stringify(data[0]))
Although this doesnt even work, I think it is the wrong approach anyway. I dont want to define the keys I want removed, I just want it to remove all keys, and keep the values.
How would I go about doing this? I am using data[0] to get the first section of the JSON, the part that contains animals. Not sure if this is correct either?
Thanks
You can simply do this, if you dont care what keys you are getting:
var collection = "";
data[0].forEach(function(row) {
var line = [];
Object.keys(row).forEach(function(key) {
line.push(row[key])
});
collection = collection + line.join(',') + '\n';
})
You will get csv string out of collection
I dont want to define the keys I want removed
You still will need to define which keys you want the values from. One could just take all the values from the object, in arbitrary order, but that's not robust against changes to the data format. Use something like
const table = data[0].map(value => [value.ID, value.animal_one, value.animal_two]);
data[0].forEach(function(row,index) {
data[0][index] = Object.values(data[0][index]);
});

How do I insert JSON data into parent child table

I have a MySQL table with the structure below
+------------------+------------------+----------------+
| comp_id | name | parent |
|------------------|------------------|----------------+
| 1 | comp1 | NULL |
+------------------+------------------+----------------+
| 2 | comp2 | 1 |
+------------------+------------------+----------------+
| 3 | comp3 | 2 |
+------------------+------------------+----------------+
| 4 | comp4 | 2 |
+------------------+------------------+----------------+
Assuming that no data has been inserted into the table. In other words, assuming that the table is empty how should i go about the following:
traverse the JSON data below for entry into the table:
{
"org_name":"paradise island",
"daughters" : [
{
"org_name": "banana tree",
"daughters": [
{"org_name":"Yellow Banana"},
{"org_name":"Brown Banana"}
]
},
{
"org_name": "big banana tree",
"daughters": [
{"org_name":"green banana"},
{"org_name":"yellow banana"},
{
"org_name": "Black banana",
"daughters": [
{"org_name": "red spider"}
]
}
]
}
]
}
what effective SQL query can I write to insert the JSON above into MYSQL database at once.
I've researched a host of resources on adjacency list model and nested models but none has been exhaustive on how inserts should be done via JSON input
If using uuidv4's as id's would be ok for you, you could just normalize your object with a recursive function, add uuid's as id's and construct the parental relationships.
After That you just bulk insert your data with whatever database client you are using.
This is an ideal use case for UUIDV4's. The chance of a collision is very unlikely, so you can consider it production safe.
You just have to make sure, that your uuid generator is random enough. Bad implementations could lead to much higher probabillities of collisions.
I suggest using the uuid package. You can look up it's sources on github. It uses nodes crypto library as random data generator which is cryptographically strong according to the documentation
Nice to know:
MongoDB's ObjectId's are created in a similar way. They also don't provide 100 percent security against collision. But they are so unlikely that they consider this chance irrelevant.
EDIT: I assumed your code runs server side in node.js. Generating uuid's in the browser is not safe, because the crypto api is still experimental and users might try to intentionally cause collisions.

Search only a specific field in ui.bootstrap typeahead

I'm refining my ui.bootstrap typeahead adding some awesomness, but I'm struggling on this one.
I've created a small Plunker to demonstrate my issue:
http://plnkr.co/edit/u2Le37?p=preview
Two words about the issue:
I have this array of objects which populate my $scope.data but I'm not able to tell typeahead to use only a particular field to search the result
$scope.data = [
{
name: 'Mario',
desc: 'Super Plumber',
id: 0,
type: 'good'
},
{
name: 'Luigi',
desc: 'Assistant Plumber',
id: 1,
type: 'good'
},
...
Whenever you search in the typeahead, you'll search for every field in the object, even type and id
I've tried, without success, solution like these:
typeahead="datum.name as ( datum.name +', '+ datum.desc)
for datum in data | filter:$viewValue | limitTo:8"
---> no change
typeahead="datum as ( datum.name +', '+ datum.desc)
for datum.name in data | filter:$viewValue | limitTo:8"
--> no match
How can I restrict the search to, let's say, the name field?
The magical line is as following
filter:{name: $viewValue}
This would limit the search to only name field. Look at this document ion for ng-filter
Object: A pattern object can be used to filter specific properties on
objects contained by array. For example {name:"M", phone:"1"}
predicate will return an array of items which have property name
containing "M" and property phone containing "1". A special property
name $ can be used (as in {$:"text"}) to accept a match against any
property of the object. That's equivalent to the simple substring
match with a string as described above.
Plunker Updated

jQuery: Get total of same string from table [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
i got a table with td's that look like this:
Country | City | Number
Thailand | Phuket | 25
Thailand | Bangkok | 12
China | Wenzhou | 1
Australia | Sydney | 2
Vietnam | Saigon | 4
China | Beijing | 3
USA | New York | 4
USA | Miami | 2
How can i get a total for each country, ie Thailand = 37 (25+12), via jQuery (or plain JS)?
It would be simple if I could define each country however these values comes from a database and it can be over 50 countries/cities so need to put each in an array first and then somehow use inArray and add Number to already existing array if it exists.
Updated with jsfiddle: http://bit.ly/1iizLAt
JavaScript objects are essentially key/value maps, so you can track the total for the countries even though you don't know in advance what their names are:
To create the map:
var countryCounts = {};
I assume you can use the jQuery API to loop through the rows of the table, and get the country name for the row; let's say you put it in the variable country. Then, to count a time you've seen the country (in the loop):
countryCounts[country] = (countryCounts[country] || 0) + 1;
(Note that this assumes there's no chance that a country will have the name of any of the properties that exist by default on Object.prototype, such as toString or valueOf.)
That line looks a bit tricky, in two ways, so here's how it works:
You can look up a property name on an object either using dot notation and a property name literal, e.g., obj.foo, or by using bracketed notation and a string, e.g. obj["foo"]. In the latter case, the string can be the result of any expression. So if country is "Thailand", countryCounts[country] looks up the property Thailand on countryCounts.
If you look up a propery that doesn't exist on the object, you get back undefined, which is a "falsey" value. JavaScript's curiously-powerful || operator returns the first non-falsey (truthy) argument you give it. So countryCounts[country] || 0 will give you countryCounts[country] if that value is truthy, or 0 if countryCounts[country] is falsey. So you end up with the current count for the country, or the number 0. Then we add one to it and store it.
Then to loop through the results in countryCounts:
for (country in countryCounts) {
if (countryCounts.hasOwnPropery(country)) {
// Use `country` and `countryCounts[country]`
}
for-in loops through the names of the enumerable properties of an object. Using hasOwnProperty is a bit paranoid here, but it's good practice; it weeds out any enumerable properties that might exist on the object's prototype. (There are no enumerable properties on Object.prototype, and if you catch someone adding any you should give them a Severe Talking To, but still it's good practice.)
In the comments below, ajax333221 suggests "normalizing" the country name a bit just in case you have both Thailand and thailand (which would be separate properties in JavaScript, since JavaScript is case-sensitive). It's a good idea. At the very least, I would make the name all-lower-case, and his/her suggestion is to remove spaces as well, which is probably a good idea and unlikely to create confusion. If you want the names for display purposes, you could store the first one you see (without making it lower case and removing spaces) in a separate map:
realCountry = /*...get the real country name...*/;
country = country.toLowerCase().replace(/ /g, '_');
if (!mungedNameMap.hasOwnProperty(country)) {
mungedNameMap[country] = realCountry;
}
Then looking up the "munged" name in mungedNameMap will give you the first un-munged version you saw.
I'd first get the data in a format that you can play with in js:
var data = [
{country: 'Thailand', city: 'Asia', number: 25},
{country: 'Thailand', city: 'Bangkok', number: 12},
{country: 'China', city: 'Wenzhou', number: 1},
{country: 'Australia', city: 'Sydney', number: 2},
{country: 'Vietnam', city: 'Saigon', number: 4},
{country: 'China', city: 'Beijing', number: 3},
{country: 'USA', city: 'New', number: 4},
{country: 'USA', city: 'Miami', number: 2}
]
Once you have that, you can sum the number for matching countries:
var counts = {};
for (var i=0; i < data.length; i++){
var row = data[i];
counts[row.country] = (counts[row.country] || 0) + 1;
}
or, if you wanted a more functional approach, you could use Array.reduce
var counts = data.reduce(function(memo, row){
memo[row.country] = (memo[row.country] || 0) + 1;
return memo;
},{});

Effecient method for structuring JSON data for querying

I'm trying to come up with an efficient method of storing static data in JSON so that it can be used in queries client-side.
Currently, this data consists of about 60 CSV files which each have approx. 2000-2200 entries each. I parse this data server-side and have a webservice that handles queries coming from the client. As mentioned, I'd like to be able to move this to the client side so that the web application could potentially work offline using the application cache.
A small sample of the data is below:
Battle Axe,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1
150,19EK,21EK,23EK,25EK,33ES,33ES,36ES,36ES,34ES,36ES,40ES,40ES,34ES,34ES,39ES,42ES,38ES,41ES,44ES,46ES
149,19ES,21ES,23ES,25ES,33ES,33ES,36ES,36ES,34ES,36ES,40ES,40ES,34ES,34ES,39ES,42ES,38ES,41ES,44ES,46ES
148,19EK,21EK,23EK,25EK,33ES,33ES,36ES,36ES,34ES,36ES,39ES,40ES,34ES,34ES,39ES,42ES,37ES,40ES,44ES,45ES
147,19ES,21ES,23ES,25ES,33ES,32ES,35ES,35ES,33ES,35ES,39ES,39ES,33ES,33ES,38ES,41ES,37ES,40ES,43ES,45ES
My original attempt at converting to JSON was based on the following:
Each file was one JSON Object (lets call this object 'weapon')
Each row in the file was another JSON object stored in an array under the corresponding weapon object
Each entry for a row was stored in a fixed length array under the row object
All of the 'weapon' objects were stored in an array.
This meant I had one array that consisted of approx. 60 objects, which in turn had on average 100 objects stored within them. Each of these 100 objects had an array of 20 objects for each entry which consisted of the actual value and some additional meta data. As you can see, I am already at 120K objects... the resulting minified json string was 3mb. Small sample below:
var weapon =
{
Name: 'Broadsword',
HitEntries: [
{
High: 150,
Low: 150,
Unmodified: false,
Hits: [ { Hits: '12', Critical: 'CK', Fail: false},...,{ Hits: '1', Critical: '', Fail: false}]
},
...
{
High: 50,
Low: 47,
Unmodified: false,
Hits: [ { Hits: '3', Critical: '', Fail: false}]
}
]
}
An example of a query that will be run is below. It will be based on the sample data csv shown above:
Battle Axe weapon is selected
A value of 147 is selected for the roll (row)
A value for 9 is selected for the armour type (column heading)
The result of the above should be 39ES (cross reference between row and heading)
At this point I decided it was probably a good idea to get some advice before heading down this path. Any input is appreciated =)
You can make a few optimizations here:
Use WebSockets to stream data if possible
Convert the data to TypedArrays (blobs) - you'll end up dealing with something like a 10K file.
Use the index DB to query if needed

Categories