RethinkDB - How to merge and average objects - rethinkdb

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')
}
})

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]);
}

GroupBy 'Linq like' in Typescript

I was looking for a 'groupBy' method in Typescript but it still seems not implemented natively.
Like:
const grouping = items.groupBy('propValue');
The grouping object should be a collection of objects like {key:string, values:any[]}
After some research, I found this solution:
https://gist.github.com/guillaumegarcia13/668518119667594fdca150ebefecd194
Anyway, I structured it a little bit more to have a better typing for my purposes.
The following prototype, supports nested properties and a optional callback parameter to compute some operation on the elements.
/**
* GroupBy array extension
*/
interface Grouping<T> {
key: string,
values: Array<T>,
computed: any
}
interface Array<T> {
groupBy(prop: T, opCallBack: (group: Grouping<T>, item: T) => any): Grouping<T>[];
}
// Nested property support
function getVal(obj, prop) {
const props = prop.split('.');
if (props.length === 1) {
return obj[prop];
} else {
return getVal(obj[props[0]], prop.slice(prop.indexOf('.') + 1, prop.length));
}
}
if (!Array.prototype.groupBy) {
// Return an array of 'Grouping' object
Array.prototype.groupBy = function (prop: string, opCallBack: (group: Grouping<any>, item: any) => any = null) {
return this.reduce((data, item) => {
// Get value
const val = getVal(item, prop);
// Search val
if (data.filter(g => g.key === val).length === 0) {
data.push({
key: val,
values: []
});
}
if(opCallBack) {
opCallBack(data.find(g => g.key === val), item);
}
data.find(g => g.key === val).values.push(item);
return data;
}, []);
}
}
/* End */
An example:
var a = [{a: 'aa', b: 45}, {a: 'aa', b: 45}, {a: 'aa', b: 2}, {a: 'cc', b: 4}, {a: 'cc', b: 45.6}, {a: 'bb', b: 1}];
console.log(a.groupBy('a', (group, item) => {
group.computed = group.computed || 0; group.computed += item.b
}));
// Log:
[
{
"key": "aa",
"values": [
{
"a": "aa",
"b": 45
},
{
"a": "aa",
"b": 45
},
{
"a": "aa",
"b": 2
}
],
"computed": 92
},
{
"key": "cc",
"values": [
{
"a": "cc",
"b": 4
},
{
"a": "cc",
"b": 45.6
}
],
"computed": 49.6
},
{
"key": "bb",
"values": [
{
"a": "bb",
"b": 1
}
],
"computed": 1
}
]
I hope it can be useful
This might not be built-in by default, but there exists a project that brings you linq-like functions to typescript. I suggest that you have a look at the linqts project at https://github.com/kutyel/linq.ts in case you want to use other methods known from LINQ.

How to use export excel and print for selected columns in datatable?

I want to use export excel and print the datatable .I use following code but not able to use both options for selected columns.
$('#example').DataTable( {
dom: 'Blfrtip',
buttons: [
{
extend: 'excel','print',
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
},
}
],
"lengthMenu": [[200, 250, 500, -1], [200, 250, 500, "All"]],
"bLengthChange" : true,
initComplete: function () {
this.api().columns().every( function () {
var column = this;
var select = $('<select><option value="">Select</option></select>')
.appendTo( $(column.footer()).empty() )
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
.search( val ? '^'+val+'$' : '', true, false )
.draw();
} );
column.data().unique().sort().each( function ( d, j ) {
select.append( '<option value="'+d+'">'+d+'</option>' )
} );
} );
}
} );
I am not figure out what happens wrong.Please help me.
{
extend: 'excel'
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
},
{
extend: 'print'
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
}
One of the most commonly used is the columns option which defines the columns that should be used as part of the export. This is given as a column-selector, making it simple to tell it if you want only visible columns, or a mix of the columns available.
and this is a simple of how it can be done :
function attachDataTable(tableId: string) {
let companyFunctionTable = $('#' + tableId).DataTable({
dom: 'Bfrtip',
buttons: [
//'csvHtml5'
{
extend: 'csvHtml5',
exportOptions: {
columns: [0, 1, 2]
},
className: 'btn btn-sm btn-outline-secondary',
text: `<i class="fa fa-file-csv"></i> ${i18next.t('Export to CSV')}`, titleAttr: i18next.t('Export to CSV'),
bom: true
},
],
"initComplete": function (settings, json) {
placeToolbarButton(tableId);
},
"paging": globalVar.dataTablesSettings.paging,
"stateSave": false,
"stateSaveParams": function (settings, data: any) {
data.search.search = "";
},
"order": [],
"lengthChange": false,
"pageLength": globalVar.dataTablesSettings.pageLength,
"language": {
"url": globalVar.dataTablesSettings.languageUrl
}
});
}
Please fellow that link :
https://datatables.net/extensions/buttons/examples/html5/columns.html
Don't forget to include the required JS libraries needed to display the exporting functionality.
Just modify your buttons option this way
buttons: [
{
extend: 'excel',
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
},
},
{
extend: 'print',
exportOptions: {
columns: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
},
},
]
elaborated #tdymicroit answer

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

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.

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.)

Resources