Filter data from arrays - sql

What I need is to sort data I get from an API into different arrays but add '0' value where there is no value at 1 type but is at the other type/s. Now is this possible with array.filter since its faster then a bunch of for and if loops ?
So let's say I get following data from SQL to the API:
Day Type Amount
-----------------------
12.1.2022 1 11
12.1.2022 2 4
13.1.2022 1 5
14.1.2022 2 9
16.1.2022 2 30
If I run this code :
this.data = result.Data;
let date = [];
const data = { 'dataType1': [], 'dataType2': [], 'dataType3': [], 'dataType4': [] }
/*only writing example for 2 types since for 4 it would be too long but i desire
answer that works for any amount of types or for 4 types */
this.data.forEach(x => {
var lastAddress = date[date.length - 1]
if (x.type == 1) {dataType1.push(x.Amount) }
if (x.type == 2) {dataType2.push(x.Amount) }}
lastAddress != x.Day ? date.push(x.Day) : '';
The array I get for type1 is [11,5]
and for type2 I get [4,9,30].
And for dates i get all the unique dates.
But the data I would like is: [11,5,0,0] and [4,0,9,30]
The size of array also has to match the size of Day array at the end.
which would be unique dates.. in this case:
[12.1.2022, 13.1.2022, 14.1.2022, 16.1.2022]
I have already tried to solve this with some for, if and while loops but it gets way too messy, so I'm looking for an alternative.
Also i have 4 types but for reference i only wrote sample for 2.

you can
first get the uniq values
loop over the data to create an array of object with
{date:string,values:number[]}
//create a function:
matrix(data:any[])
{
const uniqTypes=data.reduce((a,b)=>a.indexOf(b.type)>=0?a:
[...a,b.type],[])
const result=[]
this.data.forEach((x:any)=>{
let index=result.findIndex(r=>r.date==x.date)
if (index<0)
{
result.push({date:x.date,values:[...uniqTypes].fill(0)})
index=result.length-1;
}
result[index].values[uniqTypes.indexOf(x.type)]=x.amount
})
return result
}
//and use like
result=this.matrix(this.data);
NOTE: You can create the uniqType outside the function as variable and pass as argument to the function
stackblitz

const type1 = [];
const type2 = [];
data.forEach(item=>{
if(item.type==1){
type1.push(item.amount)
type2.push(0)
}else{
type1.push(0)
type2.push(item.amount)
}
})
console.log(type1)
console.log(type2)

Related

How to access row elements in a polars LazyFrame/DataFrame

I struggle accessing the row-elements of a Frame.
One idea I have is to filter the dataframe down to a row, convert it to a vec or something similar and access the elements this way ?!
In Panadas I used to just use ".at / .loc / .iloc / etc."; with Polars in Rust I have no clue.
Any suggestions on what the proper way to do this is ?
Thanks to #isaactfa ... he got me onto the right track. I ended up getting the row not with "get_row" but rather with "get" ... this is probably due to my little RUST understanding (my 2nd week).
Here is a working code sample:
use polars::export::arrow::temporal_conversions::date32_to_date;
use polars::prelude::*;
fn main() -> Result<()> {
let days = df!(
"date_string" => &["1900-01-01", "1900-01-02", "1900-01-03", "1900-01-04", "1900-01-05",
"1900-01-06", "1900-01-07", "1900-01-09", "1900-01-10"])?;
let options = StrpTimeOptions {
date_dtype: DataType::Date, // the result column-datatype
fmt: Some("%Y-%m-%d".into()), // the source format of the date-string
strict: false,
exact: true,
};
// convert date_string into dtype(date) and put into new column "date_type"
// we convert the days DataFrame to a LazyFrame ...
// because in my real-world example I am getting a LazyFrame
let mut new_days_lf = days.lazy().with_column(
col("date_string")
.alias("date_type")
.str()
.strptime(options),
);
// Getting the weekday as a number:
// This is what I wanted to do ... but I get a string result .. need u32
// let o = GetOutput::from_type(DataType::Date);
// new_days_lf = new_days_lf.with_column(
// col("date_type")
// .alias("weekday_number")
// .map(|x| Ok(x.strftime("%w").unwrap()), o.clone()),
// );
// This is the convoluted workaround for getting the weekday as a number
let o = GetOutput::from_type(DataType::Date);
new_days_lf = new_days_lf.with_column(col("date_type").alias("weekday_number").map(
|x| {
Ok(x.date()
.unwrap()
.clone()
.into_iter()
.map(|opt_name: Option<i32>| {
opt_name.map(|datum: i32| {
// println!("{:?}", datum);
date32_to_date(datum)
.format("%w")
.to_string()
.parse::<u32>()
.unwrap()
})
})
.collect::<UInt32Chunked>()
.into_series())
},
o,
));
new_days_lf = new_days_lf.with_column(
col("weekday_number")
.shift_and_fill(-1, 9999)
.alias("next_weekday_number"),
);
// now we convert the LazyFrame into a normal DataFrame for further processing:
let mut new_days_df = new_days_lf.collect()?;
// convert the column to a series
// to get a column by name we need to collect the LazyFrame into a normal DataFrame
let col1 = new_days_df.column("weekday_number")?;
// convert the column to a series
let col2 = new_days_df.column("next_weekday_number")?;
// now I can use series-arithmetics
let diff = col2 - col1;
// create a bool column based on "element == 2"
// add bool column to DataFrame
new_days_df.replace_or_add("weekday diff eq(2)", diff.equal(2)?.into_series());
// could not figure out how to filter the eager frame ...
let result = new_days_df
.lazy()
.filter(col("weekday diff eq(2)").eq(true))
.collect()
.unwrap();
// could not figure out how to access ROW elements
// thus I used "get" instead af of "get_row"
// getting the date where diff is == 2 (true)
let filtered_row = result.get(0).unwrap();
// within the filtered_row get element with an index
let date = filtered_row.get(0).unwrap();
println!("\n{:?}", date);
Ok(())
}

Passing an array as a parameter in a WHERE IN clause

I am trying pass in an array of strings (internal_id) to a query. The portion below counts how many elements are in the internal_id array and parameterizes them into a string so it can be compatible with SQL(Ex. if there are two IDs in internal_id, then paramString would be "$1,$2")
const { internal_id } = args; // This is an array of strings
let paramString: Array<string> = [];
for (let count = 1; count <= internal_id!.length; count++) {
paramString.push(`$${count}`)
}
let queryString = `
SELECT *
FROM customer
WHERE internal_id IN (${paramString.join(',')})`
Then I tried to pass the internal_id array as a parameter below:
const res = await db.get(queryString, [internal_id]);
But I get this error
error: bind message supplies 1 parameters, but prepared statement "" requires 2
I am not sure how to correctly pass internal_id array through db.get().
You provide and array containing another array internal_id instead of just internal_id itself.

new objects created in Vuejs getting updated to last value

I am new to Vuejs but am having an issue with some code.
I am trying to 'flatten' a series of line_items of orders into a list of items and then create meta information for each object. The first part works fine but it appears as if the reactive nature of Vuejs is causing the last value of these newly created objects to extend across all previous incarnations.
I have created a fiddle here. http://jsfiddle.net/trestles/ruj7hzcf/3/
I presume that the problem is in creating my item (~ line 70 in fiddle).
// here's the problem - why is quantity being updated to the last value?
var item = {item_id: line_item.menu_item_id, header: line_item.menu_item_header, quantity: line_item.quantity, locations: locations }
this.items.push(item);
Why in this case would item be updated to the last value?
In the table, the results are:
test item 3 5 8
spiced shrimp with horseradish cocktail sauce (per dozen) 9 5 8
dates with bacon & parmesan (per dozen) 5 5 8
marcona almonds (serves 8) 6 5 8
marinated olives (serves 8) 8 5 8
and should be
test item 3 3 0
spiced shrimp with horseradish cocktail sauce (per dozen) 9 2 7
dates with bacon & parmesan (per dozen) 5 5 0
marcona almonds (serves 8) 6 0 6
marinated olives (serves 8) 8 0 8
What am I doing wrong?
The root of most problems in your existing code is reusing the same objects - elements of this.locations and this.orderPickupTimes arrays - again and again as you pass those through createNewItem. Augment those lines in your code:
// instead of var location = this.locations[j];
var location = Object.assign({}, this.locations[j]);
// and within the internal loop
// instead of var order_pickup_time = this.orderPickupTimes[k];
var order_pickup_time = Object.assign({}, this.orderPickupTimes[k]);
... and see the difference it makes.
Still, it's only part of the problem. The key idea of your code is storing -alongside each 'item' object - the whole list of locations and pickup times, having the ones with some orders set their 'quantity' attribute changed.
But to do this, you need to differentiate between the items already processed (with their structures already filled) - and fresh items. It's cumbersome, yes, but might work with something like this:
var locations = [];
var item = this.items.find(it => it.item_id === line_item.menu_item_id);
if (item) {
locations = item.locations;
}
else {
item = {item_id: line_item.menu_item_id, header: line_item.menu_item_header, quantity: 0, locations: locations };
this.items.push(item);
}
... and have this repeated all the time you need to choose between creating a new location or orderPickupTime - or reusing the existing ones.
Here's the demo illustrating this approach.
Still, I'd change two major parts here.
First, I'd create a dedicated - private - function to group the order items by their locations and order pickup times. Something like this:
function _groupItemsByOrders(orders) {
return orders.reduce(function(groupedItems, order) {
var locationId = order.location_id;
var orderPickupTimeKey = order.order_pickup_time_short;
order.line_items.forEach(function(lineItem) {
if (!groupedItems.hasOwnProperty(lineItem.menu_item_id)) {
groupedItems[lineItem.menu_item_id] = {
header: lineItem.menu_item_header,
quantity: 0,
locations: {}
};
}
var groupedItem = groupedItems[lineItem.menu_item_id];
if (!groupedItem.locations.hasOwnProperty(locationId)) {
groupedItem.locations[locationId] = {};
}
var groupedItemLocation = groupedItem.locations[locationId];
if (!groupedItemLocation.hasOwnProperty(orderPickupTimeKey)) {
groupedItemLocation[orderPickupTimeKey] = 0;
}
groupedItemLocation[orderPickupTimeKey] += lineItem.quantity;
groupedItem.quantity += lineItem.quantity;
});
return groupedItems;
}, {});
}
Second, I'd rearranged that template so that it takes the order of locations and orderPickupTimes from header arrays, then maps it to groupedData. Something like this:
<tr v-for="(item, item_id) in groupedItems">
<td>{{item_id}}</td>
<td>{{item.header}}</td>
<td>{{item.quantity}}</td>
<template v-for="location in locations">
<td v-for="opt in orderPickupTimes">
{{item.locations[location.id][opt.short]}}
</td>
</template>
</tr>
Now, your 'mounted' hook would look like this:
mounted(){
axios.get('http://www.mocky.io/v2/59f8ceb52d00004d1dad41ec')
.then(response => {
this.locations = response.data.locations;
this.orderPickupTimes = response.data.order_pickup_times;
this.groupedItems = _groupItemsByOrders(response.data.orders);
});
}
Here's the demo.

Outputting values not equal to certain values in yii2

I would like to output variables not equal to certain values but it returns an error of
Failed to prepare SQL: SELECT * FROM `tblsuunit` WHERE `unitid` != :qp0
There are two models the first model where am getting the array of ids
public function actionSunits($id){
$unitslocation = new Unitslocation();
$id2 = Unitslocation::find()->where(['officelocationid'=>$id])->all();
foreach( $id2 as $ids){
print_r($ids['unitid']."<br>");
}
}
This outputs the ids as
8
9
11
12
13
14
16
I would then like to take the id and compare another model(units model) and get the id values not similar to the above and output then
So i have added
$idall = Units::find()->where(['!=', 'unitid', $ids])->all();
So the whole controller action becomes
public function actionSunits($id){
$unitslocation = new Unitslocation();
$id2 = Unitslocation::find()->where(['officelocationid'=>$id])->all();
foreach( $id2 as $ids){
$idall = Units::find()->where(['!=', 'unitid', $ids])->all();
}
var_dump($idall);
}
This is the units model table:
If it were working it should return 7 and 10
What could be wrong..
You should fix your code and simply use a not in condition, e.g. :
// $uls will be an array of Unitslocation objects
$uls = Unitslocation::find()->where(['officelocationid'=>$id])->all();
// $uids will contain the unitids
$uids = \yii\helpers\ArrayHelper::getColumn($uls, 'unitid');
// then simply use a not in condition
$units = Units::find()->where(['not in', 'unitid', $uids])->all();
$idall = \yii\helpers\ArrayHelper::getColumn($units, 'unitid');
Read more about ActiveQuery::where() and ArrayHelper::getColumn().
Try with:
$idall = Units::find()->where(['not in','unitid',$ids])->all();
Info: https://github.com/yiisoft/yii2/blob/master/docs/guide/db-query-builder.md
operand 1 should be a column or DB expression. Operand 2 can be either
an array or a Query object.

Datatables sorting varchar

I have a SQL query that is pulling back results ordered correctly when I try the query in MSSQL Studio.
I am using Datatables from Datatables.net and everything is working great apart from a sorting issue. I have some properties in the first column and I would like to order these like this:
1
1a
1b
2
3
4a
5
etc
However what comes back is something like this:
1
10
100
11
11a
I have looked though various posts but nothing seems to work and I believe that this must be something I should trigger from the datatables plugin but cannot find anything.
Could someone advise?
Your data contain numbers and characters, so they will be sorted as string by default. You should write your own plugin for sorting your data type. Have a look at here and here
to see how to write a plugin and how to use it with your table.
Edit: got some time today to work with the datatable stuff. If you still need a solution, here you go:
//Sorting plug-in
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
//pre-processing
"numchar-pre": function(str){
var patt = /^([0-9]+)([a-zA-Z]+)$/; //match data like 1a, 2b, 1ab, 100k etc.
var matches = patt.exec($.trim(str));
var number = parseInt(matches[1]); //extract the number part
var str = matches[2].toLowerCase(); //extract the "character" part and make it case-insensitive
var dec = 0;
for (i=0; i<str.length; i++)
{
dec += (str.charCodeAt(i)-96)*Math.pow(26, -(i+1)); //deal with the character as a base-26 number
}
return number + dec; //combine the two parts
},
//sort ascending
"numchar-asc": function(a, b){
return a-b;
},
//sort descending
"numchar-desc": function(a, b){
return b-a;
}
});
//Automatic type detection plug-in
jQuery.fn.dataTableExt.aTypes.unshift(
function(sData)
{
var patt = /^([0-9]+)([a-zA-Z]+)$/;
var trimmed = $.trim(sData);
if (patt.test(trimmed))
{
return 'numchar';
}
return null;
}
);
You can use the automatic type detection function to let the data type automatically detected or you can set the data type for the column
"aoColumns": [{"sType": "numchar"}]