Realm Filter Issue - filter

I have Inventory Model & Stock Model as realm object, whereby Inventory is to save as the product Category Name (eg. COKE / PEPSI ), and Stock as the product name (eg. Coke Regular, Coke Light, Coke Vanilla)
In my Parent Table, TableView are showing
COKE - 150 left
PEPSI - quantity left
when selectedRowAt COKE, tableView will instantiate segue to another TableView called StockTable, which will show
Coke Regular : 100 unit left
Coke Light : 30 unit left
Coke Vanilla : 20 unit left
Currently I have a button with 2 option when tapped at ParentTable :
to show all inventory with more than 1 quantity = ISSUE
to show all inventory without quantity count = PERFORMING
I am having issue to filter and query option 1, hence "totalUnit" is created with auto-update when perform realm.write to update totalUnit every single time, which is very risky for wrong information/data entry.
For me I want to query the data based on StockModel's quantity sum(property:"quantity") instead of updating the totalUnit in realm.write as it will be very risky for future and to prevent bug.
So I had created two function
showStockList_byTotalUnit() -> Results<StockModel> Currently Using
showStockList_byStockQuantity() -> [StockModel]
which both are working accordingly to what I wanted to see as result, However, I am not sure using [StockModel] is the same as Results<StockModel> or not as all my other tableView are based on Results<Object>? instead of Array.
Question
I want to remove totalUnit from InventoryModel and achieve ParentTable to show the total unit from its own StockModel's quantity
Can I convert [StockModel] to Results<StockModel>?
Is showStockList_byStockQuantity any issue that I might encounter?
class InventoryModel:Object {
#objc dynamic var name:String = ""
#objc dynamic var info:String = ""
#objc dynamic var category:String = ""
#objc dynamic var totalUnit:Int = 0 **THIS IS UPDATED BASED ON USER DELETE/ADD DATA, WISH TO REMOVE THIS**
///Childs
var ofStock = List<StockModel>()
class StockModel:Object {
#objc dynamic var name:String = ""
#objc dynamic var quantity:Int = 0
#objc dynamic var purchasePrice:Double = 0
#objc dynamic var totalCost:Double = 0
#objc dynamic var purchaseDate:Date?
///Parent
var parentInventory = LinkingObjects(fromType: InventoryModel.self, property: "ofStock")
**InventoryModel**
func showStockList_byTotalUnit() -> Results<StockModel>? {
let showActiveStock = self.ofStock.sorted(byKeyPath: "name", ascending: true).distinct(by: ["name"]).filter("totalUnit > %#",0)
return showActiveStock
}
**Inventory Model**
func showStockList_byStockQuantity() -> [StockModel] {
let allStocks = self.ofStock.sorted(byKeyPath: "name", ascending: true).distinct(by: ["name"])
var activeStocks:[StockModel] = []
for stock in allStocks.enumerated() {
let stockQuantity:Int = realm!.objects(StockModel.self).filter("name == %#", stock.element.name).sum(ofProperty: "quantity")
if stockQuantity > 0 {
activeStocks.append(stock.element)
}
}
return activeStocks
}

Let me shorten and repeat the question
The objective is to get the qty of a particular InventoryModel based on the
StockModel quantities stored in the ofStock list. (instead of storing
it in InventoryModel totalUnit property).
Using the InventoryModel and StockModel in the question, if we create an inventory model for widget
let inv = InventoryModel()
inv.name = "Widget"
and then add two StockModel objects to its ofStock List
let sm0 = StockModel()
sm0.quantity = 1
let sm1 = StockModel()
sm1.quantity = 2
inv.ofStock.append(objectsIn: [s0, s1])
If we then want to get the total inventory available for the Widget object, it's a single line
let sum: Int = realm.objects(StockModel.self).filter("ANY parentInventory.name == 'Widget'").sum(ofProperty: "quantity")
print(sum)
and the output is 3
Note that we are transversing back through the StockModels to get the parent they belong to.
EDIT
The OP also wanted to have a query to do the following (from the comments)
I want to query for all InventoryModels that have ANY stock models
that have a quantity > 0
Here's the query that returns all InventoryModel objects where any StockModels in the ofStock property have a quantity property greater than 0
let results = realm.objects(InventoryModel.self).filter("ANY ofStock.quantity > 0")

Related

Setting a "subrange" list of values in a dropdown (data validation), based on selected value from a range in Google Sheets

glorious Internet. I seek your help.
I've imported a spreadsheet from OpenOffice, and the "subrange" dropdown list (from data validation) is broken.
Basically, Column B = "Category", and Column C = "Subcategory" -- Column B data validation works properly, populates the standard Category dropdown list (it's just a Range within the same sheet, in the example below D2:E2). But then, based on the Category value, Column C should update a dropdown list of subcategories.
i.e.
row/col
D
E
2
Fruit
Vegetable
3
Apple
Carrot
4
Banana
Onion
B7 = dropdown list "Fruit / Vegetable"
I pick fruit
C7 should then update to a dropdown list "Apple / Banana"
I feel like a potential avenue might be using a Script (was looking to this for inspiration: Getting a range from a range in Google Apps scripting)
but never having used Google Scripting before, I need some basic help (like, even if I have a properly working function CreateSubranges defined in macros.gs -- but how do I get it to trigger every time a Fruit is selected?)
Any advice you have is greatly appreciated! Thank you :-)
If you don't need anything too fancy and it is for just one cell, then this formula will do. Just change the ranges to the correct sheet name/range and it will work for you. The formula gets the value selected from sheet 1 and checks if it matches Fruits or Vegetable. If it matches either of those then B3:B or C3:C is selected. I then constrain the array to only go as far as the count in B3:B...assuming that your two columns have the same number of elements. I then add this range to the data validation for C2 on Sheet one.
=Array_Constrain(ArrayFormula(if(Sheet1!B2="Fruit",B3:B,if(Sheet1!B2="Vegetable",C3:C,""))),countif(B3:B,"<>"),1)
Demo of the category and sub category
Showing each separate tab and what happens when something is selected.
If you are needing a dynamic subcategory for each row then the following onEdit script will set the data validation rules based on what was selected. This does require a small setup of creating the data validation for fruit and vegetable on Sheet2 D2 and E2.
function onEdit(e) {
var ss = e.source;
var value = e.value;
var activeSheet = ss.getSheetName();
var range = e.range;
var currentRow = range.getRow();
var currentColumn = range.getColumn();
if(activeSheet == 'Sheet1' && currentRow > 1 && currentColumn == 2 && value == 'Fruit') {
//IF THE ROW IS > 1 AND THE COLUMN = 2 AND FRUIT WAS SELECTED, THEN IT WILL COPY THE VALIDATION RULES FROM
//SHEET2 D2.
var fruitRule = ss.getSheetByName('Sheet2').getRange(2, 4).getDataValidation().copy();
range.offset(0,1).setDataValidation(fruitRule);
return;
} else if(activeSheet == 'Sheet1' && currentRow > 1 && currentColumn == 2 && value == 'Vegetable') {
//IF THE ROW IS > 1 AND THE COLUMN = 2 AND VEGETABLE WAS SELECTED, THEN IT WILL COPY THE VALIDATION RULES FROM
//SHEET2 E2.
var vegRule = ss.getSheetByName('Sheet2').getRange(2, 5).getDataValidation().copy();
range.offset(0,1).setDataValidation(vegRule);
return;
} else if(activeSheet == 'Sheet1' && currentRow > 1 && currentColumn == 2 && value == null) {
//IF THE ROW IS > 1 AND THE COLUMN = 2 AND VALUE WAS DELETED, THEN IT WILL CLEAR THE VALIDATION RULES
//FROM THE ADJACENT CELL.
range.offset(0,1).clearDataValidations();
}
}
Hope this helps! In the future, it would be much easier to show an example with a demo sheet provided.

For loop to check values from two spreadsheets

I have two spreadsheets:
Column A on sheet 6th&7thRoster lists all IDs in a sample, contains 853 items.
Column C on sheet alreadySubmitted contains the IDs of users who've completed a task. Contains 632 items.
I'm trying to parse through both columns. If a user from Column A of sheet 6th&7thRoster matches a user from Column C of sheet sandboxAlreadySubmitted, I want to write the word "Yes" on Column I of the current row of sheet 6th&7thRoster. When using the code below, I'm not seeing not seeing any instances of the word "Yes" on Column I of 6th&7thRoster, even though I know there's multiple places where that should be the case.
function checkRoster() {
var mainSheet = SpreadsheetApp.openById('XXXXXXX');
var roster = mainSheet.getSheetByName('6th&7thRoster');
var submissions = mainSheet.getSheetByName('alreadySubmitted');
var rosterLastRow = roster.getLastRow();
var submissionsLastRow = submissions.getLastRow();
var rosterArray = roster.getRange('A2:A853').getValues();
var submissionsArray = submissions.getRange('C2:C632').getValues;
var i;
var x;
for (i = 1; i < 853; i++) {
for (x = 1; x < 632; x++){
if (rosterArray[i] == submissionsArray[x]){
roster.getRange(i, 9).setValue("Yes");
}
}
}
}
Feedback on how to solve and achieve this task will be much appreciated. For confidentiality, I cannot share the original sheets.
You want to compate the values of A2:A853 of 6th&7thRoster and C2:C632 of alreadySubmitted.
When the values of C2:C632 of alreadySubmitted are the same with the values of A2:A853 of 6th&7thRoster, you want to put Yes to the column "I".
If my understanding is correct, how about this modification? Please think of this as just one of several possible answers.
Modified script:
function checkRoster() {
var mainSheet = SpreadsheetApp.openById('XXXXXXX');
var roster = mainSheet.getSheetByName('6th&7thRoster');
var submissions = mainSheet.getSheetByName('alreadySubmitted');
var rosterLastRow = roster.getLastRow();
var submissionsLastRow = submissions.getLastRow();
var rosterArray = roster.getRange('A2:A853').getValues();
var submissionsArray = submissions.getRange('C2:C632').getValues(); // Modified
// I modified below script.
var obj = submissionsArray.reduce(function(o, [v]) {
if (v) o[v] = true;
return o;
}, {});
var values = rosterArray.map(function([v]) {return [obj[v] ? "Yes" : ""]});
roster.getRange(2, 9, values.length, values[0].length).setValues(values);
}
Flow:
Retrieve values from A2:A853 of 6th&7thRoster and C2:C632 of alreadySubmitted.
Create an object for searching the values from the values of alreadySubmitted.
Create the row values for putting to 6th&7thRoster.
References:
reduce()
map()
If I misunderstood your question and this was not the direction you want, I apologize.

Avoid "Parameterless Queries Error" with Linq Let clause

I'd like my Linq query to create an additional column in the results on the fly. In this case the column is a Class object I created that will contain image info. I was wondering what the right way is of doing this:
var validPics = (from x in db.picsVotesTagsJs let picObj = new CarShowImages(x.picname) where x.enabled == 1 select x).Take(25);
var myArray = validPicSummaries.ToArray();
Line 2 gerenates the error:
Only parameterless constructors and initializers are supported in LINQ to Entities.
This is my first time using the Let clause. My queries are usually pretty simple.
Create parameterless constructor and use some public property (e.g. PicName) to set picture name to your CarShowImages object:
var validPics = (from x in db.picsVotesTagsJs
where x.enabled == 1
select new CarShowImages { PicName = x.picname }).Take(25);
var myArray = validPics.ToArray();

Get IGrouping data in Repeater ItemDataBound

I am wanting to group news articles by year in a repeater. The format would be:
2010
list of articles
2011
List of Articles
My access layer returns a flat list of news articles, specifically List. Therefore, I am grouping them and binding them to the Repeater as follows:
events = DAL.GetEvents();
var groupedNewsList = from e in events
group e by e.StoryDate.Year
into g
select new {
Year = g.Key
, Events = g
};
rptEvents.DataSource = groupedNewsList;
rptEvents.DataBind();
The problem is trying to get the List from within the ItemDataBound event. So far, I have the following:
var data = e.Item.DataItem;
System.Type type = data.GetType();
// getting the year works fine
string year = (string)type.GetProperty("Year").GetValue(data, null).ToString();
// this returns something, but I can't access any properties. I need to get
//access to the contained List<News>
var newsList = type.GetProperty("Events").GetValue(data, null);
Any ideas?
Thanks in advance!
You don't have a List<News> - you just have a grouping. If you want a List<News>, you'll need to change your query, e.g.
var groupedNewsList = from e in events
group e by e.StoryDate.Year into g
select new { Year = g.Key, Events = g.ToList() };
Note that if you're using C# 4 you could do reflection rather more easily using dynamic typing:
dynamic data = e.Item.DataItem;
string year = data.Year.ToString();
List<News> newsList = data.Events;
Alternatively, you could avoid using an anonymous type in the first place - create your own GroupedNewsList type with Year and Events properties, populate that in your query, and then cast to it in your event handler.
The "sender" object in the ItemDataBound event is the repeater -- use it to get to the data-source. If the data-source has been grouped before binding, you can compare the current value to the previous value & hide the year-field if they are equal. Like this:
MyObject item = (MyObject)item.DataItem;
Repeater repeater = (sender as Repeater);
List<MyObject> items = repeater.DataSource as List<MyObject>;
Label lblGrouping = (Label)item.FindControl("lblGrouping");
if (item.ItemIndex == 0 || item.DateField.Year != items[item.ItemIndex - 1].DateField.Year) {
lblGrouping.Text = item.DateField.Year.ToString();
}
This worked for me, as I used a table with each row being one item, and the left-most column contained the "lblGrouping" control.

Linq query, how to build nested objects from single table

I have a single table and I need to build a bunch of nested objects based on the single table.
Data:
PointA PointB Month Time Price
1 2 11 11:00 10.99
1 2 12 11:00 9.99
Objects are
POINTS {PointA, PointB, Details}
Details {Month, ExtraDetails}
ExtraDetails {Time, Price}
I want to avoid having loads of loops and if statements, so should be able to use linq to do this. but its beyond my linq experience.
edit: These need grouping aswell
any help would be great.
Thanks
Just tried out a solution:
var nestedObjects = from row in data
select new {row.PointA, row.PointB, Details = new {
row.Month, ExtraDetails = new {
row.Time, row.Price
}
}};
This is assuming that you have already got your data into data.
Group by
If you want to group the Points together, you need 'Group By':
var nestedObjects = from row in data
group row by new { row.PointA, row.PointB } into Points
select new {
Points = Points.Key,
Details = from details in Points
select new { row.Month, ExtraDetails = new {
row.Time, row.Price
}}
};
A little more complicated - of course you might want to group by month as well, in which case, you need to follow the same pattern as for the Points bit. Note, this will not create tables, because the group by doesn't quite do that, but it at least creates the structure for you.
Assuming you got your classes defined for the objects you mentioned, and you have a constructor or properties so you can propery create the object in one line you could have a LINQ query returning a list of a POINTS.
If would go something lik this :
var res =
from item in table.AsEnumerable()
select new Points(){PointA = item["PointA"];
PointB = item["PointB"];
Details = from item2 in table.AsEnumberable()
where item["PointA"] = item2["PointA"] and item["PointB"] = item2["PointB"]
select new Details(){
month=item2["month"],
extraDetails = from item3 in table.AsEnumerable()...
}
};
At the end res will be a IEnumerable of Points
I am sorry for the code, I am not at a computer with .NET 3.5 so I cannot write a proper testable query

Resources