RethinkDB: How to get data from dictionary field and transform them to lists - rethinkdb

I have objects that have a following structure:
[
{
'record': 1,
'tags': [1, 2],
'data': {
'1': 10,
'2': 15
}
},
...
{
'record': 1,
'tags': [3, 4, 5],
'data': {
'1': 100,
'2': 150
}
}
]
How can I get distinct lists of data: [10, ..., 100] and [15, ..., 150] to process each of them?
Thanks

If all data have same key, I proposed this map-reduce process.
.map(function(doc) {
return doc('data').keys().map(function(key) {
return [key, [doc('data')(key)]]
}).coerceTo('object')
})
.reduce(function(left, right) {
return left.keys().map(function(key) {
return [key, left(key).setUnion(right(key))]
})
.coerceTo('object')
})
With your data set, we have:
r.expr(
[
{
'record': 1,
'tags': [1, 2],
'data': {
'1': 10,
'2': 15
}
},
{
'record': 1,
'tags': [1, 2],
'data': {
'1': 19,
'2': 100
}
},
{
'record': 1,
'tags': [3, 4, 5],
'data': {
'1': 100,
'2': 150
}
}
]
)
.map(function(doc) {
return doc('data').keys().map(function(key) {
return [key, [doc('data')(key)]]
}).coerceTo('object')
})
.reduce(function(left, right) {
return left.keys().map(function(key) {
return [key, left(key).setUnion(right(key))]
})
.coerceTo('object')
})
Which produces:
{
"1": [10, 19, 100],
"2": [15, 100, 150]
}

You can call values on an object to get an array of values from it: https://www.rethinkdb.com/api/javascript/#values
If not all of the indexes are present as keys, you could write something like r.range(obj.keys().max()).map(function(i) { return obj(i.coerceTo('string')).default(nil); }).

If the data fields could be any string, something like this could work:
r.db('test')
.table('test')
.getAll(1, {index: 'record'})
.getField('data')
.concatMap(r.row.coerceTo('array'))
.group(r.row(0))
.concatMap([r.row(1)])
.ungroup()
Otherwise, mlucy's suggestion might be used to make it more efficient.
EDIT: on further experimentation, I could not get obj.keys() to be faster than group.

Related

laravel 9 + Vue3 + (vue-google-charts)

I'm trying to display data from laravel backend into Vue3 (vue-google-charts), but I didn't find any resources
this is a sample if my json data comming from backend
[
{
interest_oid: 1,
total_cards: 2
},
{
interest_oid: 3,
total_cards: 1
},
{
interest_oid: 5,
total_cards: 2
},
{
interest_oid: 2,
total_cards: 1
},
{
interest_oid: 4,
total_cards: 1
},
{
interest_oid: 8,
total_cards: 1
}
]
i want to display the data in GChart of type "CulomnChart"
like this one
this is my Vue component
<div class="col-lg-6">
<GChart
type="ColumnChart"
:data="chartData"
:options="chartOptions"
style="height: 100%"/>
</div>
import {GChart} from "vue-google-charts";
export default {
components: {
GChart
},
props: {
contacts_per_interests: Array,
},
data() {
return {
chartData: this.contacts_per_interests,
chartOptions: {
width: 500,
height: 300,
chart: {
title: 'Company Performance',
subtitle: 'Sales, Expenses, and Profit: 2014-2017',
}
}
}
}
}
but it's display "× Table has no columns"
any resources or suggestions please?
Your contacts_per_interests array should be look like this:
[
['interest_oid', 'total_cards'],
[1, 2],
[3, 1],
[5, 2],
[2, 1],
[4, 1],
[8, 1],
]
Adjust accordingly. Like that:
$json = '[
{
"interest_oid": 1,
"total_cards": 2
},
{
"interest_oid": 3,
"total_cards": 1
},
{
"interest_oid": 5,
"total_cards": 2
},
{
"interest_oid": 2,
"total_cards": 1
},
{
"interest_oid": 4,
"total_cards": 1
},
{
"interest_oid": 8,
"total_cards": 1
}
]';
$jsonArr = json_decode($json);
$array = [['interest_oid', 'total_cards']];
foreach ($jsonArr as $item) {
array_push($array, [$item->interest_oid, $item->total_cards]);
}

RethinkDB - How to return a sliding window on grouped data

I have some objects:
[
{ 'time': 1, 'data': { '1': 10, '2': 100} },
{ 'time': 2, 'data': { '1': 20, '2': 100} },
{ 'time': 3, 'data': { '1': 30, '2': 200} },
{ 'time': 4, 'data': { '1': 40, '2': 100} },
{ 'time': 5, 'data': { '1': 50, '2': 300} },
{ 'time': 6, 'data': { '1': 60, '2': 200} }
]
and two variables width and overlap:
width - max length of 'values' list
overlap - number of mutual values
Assume width = 3 and overlap = 2. Is there any way to obtain following?
[ { 'key': '1',
'rows': [ { 'time': 1, 'values': [10,20,30] },
{ 'time': 2, 'values': [20,30,40] },
{ 'time': 3, 'values': [30,40,50] },
{ 'time': 4, 'values': [40,50,60] }
]
},
{ 'key': '2',
'rows': [ { 'time': 1, 'values': [100,100,200] },
{ 'time': 2, 'values': [100,200,100] },
{ 'time': 3, 'values': [200,100,300] },
{ 'time': 4, 'values': [100,300,200] }
]
} ]
So far I've managed to get this:
[ { 'key': '1',
'row': { 'time': 1, 'values': [10,20,30,40,50,60] }
},
{ 'key': '2',
'row': { 'time': 1, 'values': [100,100,200,100,300,200] }
} ]
using this:
.concatMap(function(item) {
return item('data').keys().map(function(key) {
return {
'key': key,
'row': {
'time': item('time'),
'values': [item('data')(key)]
}
}
})
})
.group('key')
.ungroup()
.map(function(list) {
return list('reduction').reduce(function(left, right) {
return {
'key': left('key'),
'row': {
'time': left('row')('time'),
'values': left('row')('values').union(right('row')('values'))
}
}
})
})
Maybe I need to add something or to change everything?
Thanks.
This is pretty similar to mlucy's solution, but it doesn't assume that the time fields are consecutive integers. The data is sorted by time before the concatMap below - for large datasets, this should be done with an index.
r.expr(data)
.orderBy('time')
.concatMap(function (row) {
return row('data').coerceTo('array').map(function (pair) {
return { key: pair(0), value: pair(1), time: row('time') };
});
})
.group('key')
.ungroup()
.map(function (g) {
let rows = g('reduction').count().do(function (c) {
return r.range(c.sub(2)).map(function (i) {
let values = r.range(3).map(function (j) {
return g('reduction')(i.add(j))('value');
}).coerceTo('array');
return { 'time': g('reduction')(i)('time'), 'values': values };
}).coerceTo('array');
});
return { key: g('group'), rows: rows };
})
You probably want something like this:
r.table('test').orderBy('time').concatMap(function(row) {
return row('data').coerceTo('array');
}).group(function(pair) {
return pair(0);
}).map(function(pair) {
return pair(1);
}).ungroup().map(function(group) {
return {
key: group('group'),
rows: group('reduction').do(function(x) {
return r.range(x.count().sub(2)).map(function(i) {
return {
time: i,
values: r.range(3).map(function(o) {
return x(i.add(o));
}).coerceTo('array')
};
}).coerceTo('array');
})
};
})
(Where the .sub(2) and .range(3) need to change based on the width and overlap.)

RethinkDB - How to merge and average objects

I have documents structured as follows:
[
{
'post': 1,
'tags': [1, 2],
'poll': {
'a': 2,
'b': 2
}
},
{
'post': 1,
'tags': [3],
'poll': {
'a': 4,
'b': 6
}
},
]
How can I merge them so that tags will be the union of all tags across these posts and poll will be averaged? From the example above the result should look like this:
[
{
'post': 1,
'tags': [1, 2, 3],
'poll': {
'a': 3,
'b': 4
}
}
]
Thanks.
I think we can use map-reduce since we turn many document into a single one. Something like this should works:
r.expr([
{
'post': 1,
'tags': [1, 2],
'poll': {
'a': 2,
'b': 2
}
},
{
'post': 1,
'tags': [3],
'poll': {
'a': 4,
'b': 6,
}
},
])
.merge(function(doc) {
return {
'poll': doc('poll').coerceTo('array').map(function(poll) { return [poll(0), [poll(1)]] }).coerceTo('object')
}
})
.reduce(function(left, right) {
return {
'post': left('post'),
'tags': left('tags').setUnion(right('tags')),
'poll': left('poll').keys().setUnion(right('poll').keys()).map(function(k) {
return [k,
left('poll')(k).default([]).union(right('poll')(k).default([]))
]
})
}
})
.merge(function(doc) {
return {
poll: doc('poll').map(function(poll) {
return [poll(0), poll(1).avg()]
}).coerceTo('object')
}
})
This produces:
{
"poll": {
"a": 3,
"b": 4
},
"post": 1,
"tags": [1, 2, 3]
}
Fixed version originally proposed by kureikain:
r.expr([
{
'post': 1,
'tags': [1, 2],
'poll': {
'a': 2,
'b': 2
}
},
{
'post': 1,
'tags': [3],
'poll': {
'a': 4,
'b': 6,
}
},
{
'post': 1,
'tags': [2],
'poll': {
'a': 1,
'b': 3,
}
},
])
.merge(function(doc) {
return {
'poll': doc('poll').coerceTo('array').map(function(poll) { return [poll(0), [poll(1)]] }).coerceTo('object')
}
})
.reduce(function(left, right) {
return {
'post': left('post'),
'tags': left('tags').setUnion(right('tags')),
'poll': left('poll').keys().map(function(k) {
return [k, left('poll')(k).default([]).union(right('poll')(k).default([]))]
}).coerceTo('object')
}
})
.merge(function(doc) {
return {
poll: doc('poll').keys().map(function(k) {
return [k, doc('poll')(k).avg()]
}).coerceTo('object')
}
})

Merging and updating hash keys in Redis with Ruby

I have a list the following hashes:
{
key_main1: {
k1: 1,
k2: 11,
k3: 33,
k4: 146,
k5: 12,
# etc
},
key_main2: {
k1: 1,
k2: 11,
k3: 33,
k4: 146,
k5: 12,
# etc
},
# etc
}
which is save in a redis as json:
redis_key1 = "redis_key1"
redis.set("redis_key", my_hash.to_json)
redis.get("redis_key") # =>
"{"key_main":{"k1":1,"k2":11,"k3":33,"k4":146,"k5":12}}"
The hashes have the same structure but can have different keys. On each iteration I want to update the existing keys by summing up the values of "k"s or/and insert the keys that don't exist:
So the second hash in the list looks like this
{
key_main1: {
k3: 44,
k4: 14,
k18: 99
},
key_main3: {
k2: 77
}
}
Then after the seconds iteration the result in Redis will look like the following:
{
key_main1: {
k1: 1,
k2: 11,
k3: 33,
k4: 160,
k5: 12,
k18: 99
},
key_main2: {
k1: 1,
k2: 11,
k3: 33,
k4: 146,
k5: 12
},
key_main3: {
k2: 77
}
}
What's the easiest and best way to do it? Do I have to parse (restore)json in each iteration in order to check if the keys exist and update or insert them?
The best way is to parse the JSON back into a hash, otherwise you will have a really hard time figuring out how to combine the hashes.
To combine them, the best and easiest is with Hash#merge:
h1 = { m1: { k1: 10, k2: 20, k3: 30 }, m2: { k1: 11, k2: 12 } }
h2 = { m1: { k1: 500, k2: 5, k4: 40 }, m3: { k2: 123 } }
pp h1.merge(h2) { |key, v1, v2|
v1.merge(v2) { |key, v1, v2| v1 + v2 }
}
=> { :m1 => { :k1 => 510,:k2 => 25, :k3 => 30, :k4 => 40 },
:m2 => { :k1 => 11, :k2 => 12},
:m3 => { :k2 => 123 } }
This code assumes the keys in h1 and h2 always contain a hash with integer keys.

Merge two hashes of hashes, without overlap

I have two hashes of hashes that look basically like:
sales = {
"2013-03-15": {sales: 5},
"2013-03-14": {sales: 3},
"2013-03-12": {sales: 8},
... }
and
views = {
"2013-03-15": {views: 30},
"2013-03-14": {views: 23},
"2013-03-13": {views: 35},
... }
How can I merge them into a single hash that looks like:
data = {
"2013-03-15": {views: 30, sales: 5},
"2013-03-14": {views: 23, sales: 3},
"2013-03-13": {views: 35, sales: 0}, # or just {views: 35}
... }
Basically, I need to keep the keys and all the data intact.
data = views.merge sales Seems to override all the views data, leaving me with basically just the sales hash.
Edit: I can also convert the sales or views hashes into simple hashes (not hashes of hashes), but I still don't know a good way to proceed.
This:
sales.merge(views) { |k, o, n| o.merge(n) }
Runnable example:
views = {
"2013-03-15" => {:views=> 30},
"2013-03-14" => {:views=> 23},
"2013-03-13" => {:views=> 35},
}
sales = {
"2013-03-15" => {:sales=> 5},
"2013-03-14" => {:sales=> 3},
"2013-03-12" => {:sales=> 8},
}
puts sales.merge(views) { |k, o, n| o.merge(n) }
=> {"2013-03-15"=>{:sales=>5, :views=>30}, "2013-03-14"=>{:sales=>3, :views=>23}, "2013-03-12"=>{:sales=>8}, "2013-03-13"=>{:views=>35}}

Resources