Using Linq to shape structure for Json serialization - json

I have a pretty simple structure that looks something like this:
var list = new List<CategoryInTimeItem>
{
new CategoryInTimeItem { Name = "Food", Year = 2012, Month = 1, Amount = 100 },
new CategoryInTimeItem { Name = "Food", Year = 2012, Month = 2, Amount = 110 },
new CategoryInTimeItem { Name = "Food", Year = 2012, Month = 3, Amount = 130 },
new CategoryInTimeItem { Name = "Food", Year = 2012, Month = 4, Amount = 130 },
new CategoryInTimeItem { Name = "Transport", Year = 2012, Month = 1, Amount = 1000 },
new CategoryInTimeItem { Name = "Transport", Year = 2012, Month = 2, Amount = 1101 },
new CategoryInTimeItem { Name = "Transport", Year = 2012, Month = 3, Amount = 1301 },
new CategoryInTimeItem { Name = "Transport", Year = 2012, Month = 4, Amount = 1301 }
};
I want to reshape this structure so that when it get's serialized to json the result should look like this, one array for each name:
[
[["2012-1", 100], ["2012-2", 110], ["2012-3", 130], ["2012-4", 130]],
[["2012-1", 1000], ["2012-2", 1101], ["2012-3", 1301], ["2012-4", 1301]]
]
My linq query looks like this:
result.Values =
from d in list
orderby d.Name , d.Year , d.Month
group d by d.Name
into grp
select new[]
{
grp.Select(y => new object[] {y.DateName, y.Amount})
};
This almost works, however I get an extra "level" of arrays, so when serialized to json the result looks like this:
[
[[["2012-1", 100], ["2012-2", 110], ["2012-3", 130], ["2012-4", 130]]],
[[["2012-1", 1000], ["2012-2", 1101], ["2012-3", 1301], ["2012-4", 1301]]]
]
What am I doing wrong here?

You've almost been there, just instead of
from d in list
...
select new[]
{
grp.Select(y => new object[] {y.DateName, y.Amount})
}
simply:
from d in list
...
select grp.Select(y => new object[] {y.DateName, y.Amount}).ToList()
You just added an unnecessary level of array at the end.

Related

Can't figure out how to add duplicate count to end of array

I think I'm totally overthinking this but I can't get out of my head. In Google Sheets, I have a list of work orders that look like this:
Work ID Location Start Time Officer Count
123 North 1100
123 North 1100
123 North 1100
222 South 1200
999 North 1400
999 North 1400
333 South 1200
Each work order always has one Officer assigned to it, so I need to count the duplicated work orders and push them to the end of the array under "Officer Count" so it shows how many total Officers needed. For example, 123 would need 3, 222 needs 1, 999 needs 2, and 333 needs 1.
However, the code I have now pops out the right count, just out of order from the original 2D array so I can't push it to the end of the array. Any suggestions?
var rowRange = sheet.getRange(2, 1, 7, 3).getValues();
//Create Work Number array (1D array)
var oneDArr = rowRange.map(function(row){return row[0];});
//Create object to count number of officers to Work Number
var counts = {};
oneDArr.forEach(function(x) { counts[x] = (counts[x] || 0)+1; });
//Create Array from Object
var array = [];
var array = Object.entries(counts);
//Set Values to correct location
// sheet.getRange(11, 1, array.length, array[0].length).setValues(array);
}
Description
Here is an example script to get the count of work orders and place them in the original array.
Code.gs
function test () {
try {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
let values = sheet.getDataRange().getValues();
values.shift(); // remove the headers
let ids = values.map( row => row[0] );
ids = [...new Set(ids)]; // create an array of unique ids
console.log(ids);
let count = ids.map( id => 0 ); // initialize count to 0
values.forEach( row => { let index = ids.indexOf(row[0]);
count[index]++;
});
console.log(count);
// now let put back into spreadsheet
ids.forEach( (id,i) => { let j = values.findIndex( row => row[0] === id );
values[j][3] = count[i]; } );
console.log(values);
}
catch(err) {
console.log(err);
}
}
Execution log
8:46:20 PM Notice Execution started
8:46:23 PM Info [ 123, 222, 999, 333 ]
8:46:23 PM Info [ 3, 1, 2, 1 ]
8:46:23 PM Info [ [ 123, 'North', 1100, 3 ],
[ 123, 'North', 1100, '' ],
[ 123, 'North', 1100, '' ],
[ 222, 'South', 1200, 1 ],
[ 999, 'North', 1400, 2 ],
[ 999, 'North', 1400, '' ],
[ 333, 'South', 1200, 1 ] ]
8:46:21 PM Notice Execution completed
References
Array.shift()
Array.map()
Set Object
Array.forEach()
Array.indexOf()
Array.findIndex()

How to do 2 groupings in a SQL query, and export to JSON

Here's an example, in csv form, of my mySQL store when.
trek_id, number, name, value, units, userID, deviceID, lat , lng
--------------------------------------------------------------------------------
88, 4, Hum , 720, PPB , 96, 1, 40.0215268, -105.2177324
88, 4, PM10, 720, PPB , 96, 1, 40.0215268, -105.2177324
88, 6, Pres, 730, PPB , 96, 1, 40.0215299, -105.2177096
88, 6, PM10, 730, PPB , 96, 1, 40.0215299, -105.2177096
So a trek_id has multiple number values, and each number contains multiple measurements. I would love to know how to query this so I could eventually insert into a json object that looks like this:
{
"88":{
"4":{
"lat":"40.0215268",
"lng":"-105.2177324",
"userID":96,
"deviceID":"1",
"measurements":[
["Hum",
"PPB",
720
],
["PM10",
"PPB",
720
]
]
},
"6":{
"lat":"40.0215299",
"lng":"-105.2177096",
"userID":96,
"deviceID":"1",
"measurements":[
["Pres",
"PPB",
730
],
["PM10",
"PPB",
720
]
]
}
}
}
So essentially I need to group on trek_id and then again on number.
Ok, first things first: SQL results are FLAT tables. They don't have sub groups or sub array. So you need to handle all sub-grouping in the display code.
Second: You existing data is already perfect for the DISPLAY code (let's say PHP) to create a multi-dimensional array to transform into JSON.
--- HINT PSEUDO_CODE ---
<?php
// assume $results contains your data AND SORTED BY trek_id, number, name
$cur_trek = null;
$cur_number = null;
$json = array();
foreach ($results as $res) {
if ($cur_trek != $res['trek_id']) {
$cur_trek = $res['trek_id'] ;
$json[$cur_trek] = array();
$cur_number = null;
}
if ($cur_number != $res['number']) {
$cur_number = $res['number'];
$json[$cur_trek][$cur_number] =
array(
'lat' => $res['lat'],
'lng' => $res['lng'],
'userID' => $res['userID'],
'deviceID' => $res['deviceID'],
'measurements' => array();
);
}
$json[$cur_trek][$cur_number]['measurements'][] =
array($res['name'], $res['units'], $res['value']);
}
$json = json_encode($json);

how select column in flask sqlalchemy

How can i select a name column from another table instead of id ,
example :
class Attendance(db.Model):
__tablename__ = 'zk_attendance'
id = db.Column(db.Integer,primary_key=True)
uid = db.Column(db.Integer,db.ForeignKey('zk_users.uid'))
date = db.Column(db.Date)
time = db.Column(db.Time)
device = db.Column(db.Integer,db.ForeignKey('devices.id'))
user = db.relationship('Users',backref=db.backref('user', lazy='dynamic'))
class Users(db.Model):
__tablename__ = 'zk_users'
uid = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String)
And if i want to select all attendance :
#app.route('/api/attendance/<string:date_from>/<string:date_to>',methods=['GET'])
def get_attend_date_date(date_from,date_to):
data = db.session.query(Attendance).filter(Attendance.date.between(date_from,date_to)).order_by(Attendance.date,Attendance.time)
attendance_schema = AttendanceSchema(many=True)
data = attendance_schema.dump(data).data
return jsonify({'attendance':data})
OUTPUT
{
"attendance": [
{
"Device": 4,
"date": "2016-01-18",
"id": 18805,
"time": "00:49:00",
"user": 30025
},
{
"Device": 4,
"date": "2016-01-18",
"id": 18902,
"time": "00:49:00",
"user": 30045
},
BUT
am getting the user uid , i want to return the user.name
Am using flask_marshmallow to serialize the data before send it as josn , to be able send the user name , i have to nest the name from the users schema as the following :
class UsersSchema(ma.Schema):
name = fields.String(dump_only=True)
class AttendanceSchema(ma.Schema):
date = fields.Date(dump_only=True)
user = fields.Nested(UsersSchema)

LINQ-to-SQL GroupBy across Many-to-Many Divide

I have the following issue to solve.
I have an IQueryable list of Invoices, each tied to an Account. Each Account can have multiple Structures, and Accounts and Structures are tied together by a table called StructureAccount. It looks like this:
Invoice ---> Account <-----StructureAccount ----> Structure
I want to query my IQueryable list of Invoices and group by Structure.StructureID or StructureAccount.StructureID. But, because any given invoice can be tied to multiple Structures the best I can get is a LIST of StructureIDs, and therefore my GroupBy is not working.
I feel like I am missing an obvious solution to this.
I should note that I understand that the data in any one Invoice would be counted multiple times if the Invoice were tied to more than one Structure, and this is "solved" by a "PercentAllocationtoStructure" value in the table StructureAccount.
I hope I did a good enough job explaining this problem. Let me know if not.
Hmmm...I might be missing something, but doesn't the following work?
var q = from i in Invoice
join a in Account
on i.AccountID equals a.AccountID
join sa in StructureAccount
on i.AccountID equals sa.AccountID
join s in Structure
on sa.StructureID equals s.StructureID
group i by s.StructureID;
I tested it on the following dummy data:
var Invoice = new [] {
new { InvoiceID = 1, AccountID = 1 },
new { InvoiceID = 2, AccountID = 2 },
new { InvoiceID = 3, AccountID = 3 },
new { InvoiceID = 4, AccountID = 1 },
new { InvoiceID = 5, AccountID = 2 },
new { InvoiceID = 6, AccountID = 3 }
};
var Account = new [] {
new { AccountID = 1 },
new { AccountID = 2 },
new { AccountID = 3 },
};
var StructureAccount = new [] {
new { AccountID = 1, StructureID = 2 },
new { AccountID = 1, StructureID = 3 },
new { AccountID = 2, StructureID = 2 },
new { AccountID = 3, StructureID = 1 },
new { AccountID = 3, StructureID = 2 },
};
var Structure = new [] {
new { StructureID = 1 },
new { StructureID = 2 },
new { StructureID = 3 }
};
And it returns:
StructureID = 2:
InvoiceID's: 1,2,3,4,5,6
StructureID = 3:
InvoiceID's: 1,4
StructureID = 1:
InvoiceID's: 3,6
I'll assume you have the following starting point:
IQueryable<Invoice> _invoices;
First, you need to get a list of all the items that you will be iterating over:
IQueryable<Account> _accounts = _invoices.Select(myInvoice => myInvoice.Account).Distinct();
IQueryable<StructuredAccount> _structuredAccounts = _accounts.SelectMany(myAccount => myAccount.StructuredAccounts);
IQueryable<Structure> _structures = _structuredAccounts.Select(myStructuredAccount => myStructuredAccount.Structure).Distinct();
Next, you need to go back and join your Structure objects to the respective Invoice objects.
For this, you'll:
Get a set of {Structure, Account} pairs:
var structureAccountJoin = _structures.Join(_structuredAccounts, _structure => structure.StructuredID, _structuredAccount => _structuredAccount.StructuredID, (structure, structuredAccount) => new { Structure = structure, Account = structuredAccount.Account });
Get a set of {Structure, Invoice} pairs:
var structureInvoiceJoin = structureAccountJoin.Join(_invoices, myObj => myObj.Account.AccountID, invoice => invoice.AccountID, (myObj, invoice) => new { Structure = myObj.Structure, Invoice = invoice});
Finally, you can group everything by the Structure object:
IQueryable<IGrouping<Structure, Invoice>> groupByStructure = structureInvoiceJoin.GroupBy(obj => obj.Structure, result => result.Invoice);
(GroupBy documentation: http://msdn.microsoft.com/en-us/library/bb534304.aspx)
Now, you can access everything as follows:
foreach(IGrouping<Structure, Invoice> groupEntry in groupByStructure)
{
Structure currentGrouping = groupEntry.Key;
foreach(Invoice inv in groupEntry)
{
// do something;
}
}
As a note, this is a very complex script that requires a lot of steps if you don't have access to the tables directly. You may want to look into creating a StoredProcedure for this instead, as it will be more efficient and you'll be able to use SQL Joins instead. If you have only an IQueryable<Invoice> to work with and access to nothing else, there is probably a design problem somewhere in your architecture.
Nevertheless, this is the way to make it work based on your requirements, if I read them correctly.

Does linq to sql have a with ties option?

I'm trying to move the following query to Linq-to-sql, is it possible?
select * from (
Select top (#Percent) percent with ties *
from(
Select distinct
LoanNumber as LoanNo
From CHE
Left Join RecordingInfo as Rec
On CHE.LoanNumber = Rec.LoanNo
Where Channel = 'LINX'
and CHE.Doc in ('MTG','MOD')
and Rec.LoanNo is null
and LoanNumber >= '#LoanNo'
) A
order by LoanNo #Order
) B
order by LoanNo
I have not seen anyway to do with ties in linq.
I think something like this will work for you.
public static IQueryable<T> TopPercentWithTies<T, TKey>(this IOrderedQueryable<T> query, Expression<Func<T, TKey>> groupByExpression, double percent)
{
var groupedQuery = query.GroupBy(groupByExpression);
int numberToTake = groupedQuery.Count() * percent / 100;
return groupedQuery.Take(numberToTake).SelectMany(t => t);
}
I only tested it with IEnumerable, so I don't know for sure that it'll work properly with IQueryable. I also sorted the list before calling TopPercentWithTies().
Here's the code I used to test it.
int percent = 50;
var people = new []
{
new { Age = 99, Name = "Adam" },
new { Age = 99, Name = "Andrew" },
new { Age = 89, Name = "Bob" },
new { Age = 50, Name = "Cecil" },
new { Age = 50, Name = "Doug" },
new { Age = 50, Name = "Everett" },
new { Age = 35, Name = "Frank" },
new { Age = 25, Name = "Greg" },
new { Age = 15, Name = "Hank" }
};
var sortedPeople = people.AsQueryable().OrderByDescending(person => person.Age);
var results = sortedPeople.TopPercentWithTies(person => person.Age, percent);
foreach (var person in results)
Console.WriteLine(person);
Hope it helps or at least gets you in the right direction. You may want to tweak the logic for calculating numberToTake.