I am receiving an array in route data containing different string values. Since I don't know how many string values I am going to receive, I cant have a definite Where function. Hence I am looping through the array and making multiple calls to DB set, fetching the result and adding it into another Master List. Please see code below.
public JsonResult GetModels(string brand)
{
string[] brands = brand.Split(seperator);
MasterList.Clear();
for (int i = 0; i < brands.Length; i++)
{
tempString = brands[i];
tempList = db.Devices.Where(r => r.Brand.Equals(tempString)).Select(r => new MySelectList { Value = r.PhoneModel, Text = r.PhoneModel }).Distinct().ToList();
for (int a = 0; a < tempList.Count; a++)
MasterList.Add(tempList[a]);
}
return Json(MasterList, JsonRequestBehavior.AllowGet);
}
Is there a way I can somehow avoid looping through the array and use it directly in Where function? Meaning Where function can look into values of the array and return result based on it.
You can do this by using Contains. This effectively generates an SQL IN clause:
tempList = db.Devices.Where(r => brands.Contains(r.Brand));
Related
I'm trying to copy one field to another field in the same table with 10,000 + records, in batches of 50 using the Scripting App.
What am I doing wrong in this code block? It only copies the first record. If I remove the await, it'll copy 15 records then stop.
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
let records = query.records;
updateLotsOfRecords(records);
async function updateLotsOfRecords(records) {
let i = 0;
while (i < records.length) {
const recordBatch = records.slice(i, i + 50);
for (let record of recordBatch) {
let sourceValue = record.getCellValue('Merchant');
await table.updateRecordAsync(record, { 'LogoBase64': sourceValue });
}
i += 50;
}
}
you should use updateRecordsAsync function, not updateRecordAsync
When using single update function in loop, there is no sense to divide it into batches.
You exceed some limit of calls per second, that's why it stops.
For multiple updates, you need to use updateRecordsAsync, like this
while (recordsToWrite.length > 0) {
await updates.updateRecordsAsync(recordsToWrite.slice(0, 50));
recordsToWrite = recordsToWrite.slice(50);
}
Data that you should pass to it, more complex. I learned JS for 3 months and still have difficulties understandins all these "arrays of arrays of objects, passed via object's property". But that's the key to unerstand JS.
It's quite hard to leave basic/pascal habits, with plenty of inserted FOR loops, and GOTO sometimes))
I think, you already found the answer for 2 months, so my answer may be useless, but when i write it here, maybe i understand it better for myself. And help to some beginners also.
For single write, you pass (record, Object), where object is {field:'Value}
For multiple, you should pass
Array of Objects, where
Object is {id:recordID, fields:{object2}} , where
object2 is array of obj3 [ {obj3},{obj3}, {obj3} ], where
obj3 is a { 'Name or ID of field': fieldvalue }
you script might be:
let query = await view.selectRecordsAsync();
let updates=query.records.map(rec=>{
Map method can be applied for arrays, and 'query.records' is array of records. Here
'rec' is loop variable inside this "arrowfunction"
now let's create obj3 , in our case { 'Name or ID of field': fieldvalue }
{'LogoBase64':rec.getCellValue('Merchant')}
wrap it into fields property
fields:{'LogoBase64':rec.getCellValue('Merchant')}
and add record id
wrapping as Object.
To avoid complex string with linebreaks, and to make object creation easier, we can do it with function:
{rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
fuction myObj(rec){return {rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
map(rec=>myObj(rec)) - can be written as map(myObj)
we need array of objects, and map method gets first array, doing something with each element and return other array, of results. like we need.
and now finally we get
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
function myObj(rec){return {'id':rec.id,'fields':{'Logobase64':rec.getCellValue('Merchant')}}};
let updates=query.records.map(myObj);
while (updates.length > 0) {
await table.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50); }
So I have two arrays a name array and a values array they are a string and double respectively. I want to be able for a user to type in a textfield and to iterate though the name array until there is a match then output the value that has the same number as the name to be outputted
this is the code i have:
for(var i = 0; i<name.count; i++){
if name[i] == typeFood{
yieldOutput == percent[i]
}
}
First, use find to locate the location of the value in the name array, then use the returned index to look up the percent:
if let idx = find(name, typeFood) {
yieldOutput = percent[idx]
}
You can also combine the two actions together with map to declare an output variable using let:
if let foodPercentage = find(name, typeFood).map({ percent[$0] }) {
// foodPercentage will have a value here
}
else {
// not-found logic here
}
Given a Map, assignment, what is the fastest way to check if it contains any duplicate values in Dart? I am currently using a Set formed from the Map's values and checking its length against the original Map, which works of course, but I'm wondering if there's an especially performant alternative.
Set d = new Set.from(assignment.values);
if (d.length < assignment.length) {
return false; // indicates has duplicates in this context
}
EDIT:
Tried #mezoni's solution modified for my program, but it actually ran a bit slower than my original version. It probably has more to do with constant times than anything else.
List values = new List.from(assignment.values);
Set set = new Set();
for (var i = 0; i < assignment.length; i++) {
if (!set.add(values[i])) {
return false;
}
}
Complexity wise you won't be able to get anything faster. Creating the Set and filling it with the values of the Map is linear in the number of elements. Clearly you have to run through all the values, so you can't do any better than that.
Maybe you could find a solution with a smaller constant factor, but that's not clear. In particular for larger sets I think the Set solution is pretty efficient.
This is more of a algorithms question than a Dart question. In any case, you have to check every value against the others, giving n-1 + n-2 + ... + n-(n-1) checks, or n^2/2. Programmatically, it's easy to create a set, but you could also generate an array, sort the array, and then iterate once to check for duplicates. That finishes in O(n log n).
Fastets way (if you realy need better performance):
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var length = values.length;
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var i = 0; i < length; i++) {
statistics++;
if(!set.add(values[i])) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}
Output:
Duplicate: true
Performed in 4 iteration(s) from 8 possible
P.S.
The first example can be recommended to use with List values.
But because Map.values not a List but Iterable then it would be more efficient do not convert them to List but use as is.
Here is modified sample for use with Iterable objects.
It will be work faster because in this algorithm not required convert all values to the List object because it not want using of all elements without exception.
Instead it wants use as less as possible access operation on original source. If the source supports lazy operation of the access to values (as Iterable) this will be even better.
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var assignment = {};
var length = values.length;
var key = 0;
for(var value in values) {
assignment[key++] = value;
}
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var value in assignment.values) {
statistics++;
if(!set.add(value)) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}
Is there a generic way to retrieve PropertyInfo based on a string value alone, when deeper than one level.
I assume this is probably simple enough, but my search results are only as good as my search criteria, and I think I am having an issue articulating the proper keywords to get search results for what I am after.
I would like to be able to do something like the following (which works perfect if the key is for a direct property / one level - ie key = 'firstName'):
public static PropertyInfo (this HtmlHelper htmlHelper, string key) {
PropertyInfo pInfo = htmlHelper.ViewData.Model.GetType().GetProperty(key);
return pInfo;
}
But is there a way for me to return the PropertyInfo based on a string alone
when Key equals something more complex, such as nested classes, objects, lists, etc...:
key = "somelist[0].myproperty"
key = "Items[0].someotherlist[1].someproperty" (where Items is defined as List<Item> Items {get; set;}, someotherlist is defined similarly)
Can the method be generic enough to essentially drill down as many levels as needed (defined)?
So here is what I came up with... this is about to get wordy, and mostly 'stream of thought'
I have custom HtmlHelperExtension, and within it :
PropertyInfo[] pInfoArray = htmlHelper.ViewData.Model.GetType().GetProperties();
PropertyInfo pInfo = GetPropertyInfo(pInfoArray, key);
This GetPropertyInfo() method takes the key, and the PropertyInfo array, cycles through the properties, until the keypart (using regex to remove any indication of an array from the string, so I am left with only the property) matches the property name. On Match, determine if this is the first cycle in the loop, and if so assign the matched property to my Temp Type and PropertyInfo variables. If keyParts are remaining to loop through, subsequent loops now use previously set temp variables and the for loop index [i] to iterate / drill down the class structure. Each time setting the pInfoTemp variable, and then pTypeTemp so the next loop can use where it left off.
private static PropertyInfo GetPropertyInfo(PropertyInfo[] pInfoArray, string key)
{
PropertyInfo pInfo = null;
string[] keyParts = key.Split('.');
Regex arrayRgx = new Regex("\\[\\d*\\]");
PropertyInfo pInfoTemp = null;
Type pTypeTemp = null;
foreach (PropertyInfo prop in pInfoArray)
{
string keyPartsTrimmed = arrayRgx.Replace(keyParts[0], ""); // removes '[#]' from string
if (keyPartsTrimmed == prop.Name) // match property name
{
for (int i = 0; i < keyParts.Count(); i++)
{
if (i == 0) // initial item [0]
{
pTypeTemp = prop.PropertyType; // gets [0]'s type
pInfoTemp = prop; // assigns [0]'s property info
}
else
{
pInfoTemp = GetNestedPropertyInfo(pTypeTemp, arrayRgx.Replace(keyParts[i], "")); // gets [i]'s property info for return or next iteration
pTypeTemp = pInfoTemp.PropertyType; // gets [i]'s type for next iteration
}
}
pInfo = pInfoTemp;
break;
}
}
return pInfo;
}
This next method is invoked by the previous for grabbing nested property info, more importantly for detecting whether the passedItemType is a List (without this, it fails to work correctly as it is unable to find the property asked for in a List<> Type. I need to know what the List item Type is.
private static PropertyInfo GetNestedPropertyInfo(Type passedItemType, string passedProperty)
{
PropertyInfo pInfoOut = null;
if (passedItemType.IsGenericType && passedItemType.GetGenericTypeDefinition() == typeof(List<>))
{
Type itemType = passedItemType.GetGenericArguments()[0];
pInfoOut = itemType.GetProperty(passedProperty);
}
else
{
pInfoOut = passedItemType.GetProperty(passedProperty);
}
return pInfoOut;
}
This currently suits my requirements as they are today, and I have tested it with the following properties, lists, subclasses, subclasses with lists, etc.. to 4 levels deep, but should function properly no matter the depth:
firstName
lastName
Items[1].sprocket
subClass.subClassInt
subClass.mySubClassObj.sprocketObj
subClass.ItemsInMySubClass[1].sprocket
subClass.ItemsInMySubClass[0].mySubClassObj.widgetObj
subClass.ItemsInMySubClass[2].mySubClassObj.sprocketObj
If anyone has a better solution, or see any potential issues with what I have, I welcome the feedback.
The best way in your case is to make a parser that split that expression.
What is the best practice in Dart when dealing with classes as data records?
To Elaborate: When writing an app, it is likely that a class for a table row will be created. As in
class Item { int itemid, String itemName, double score }
Item item = new Item();
This allows compile time catching of any typos etc. in Dart. (Unlike using a class that relies on NoSuchMethod.)
It will also need a corresponding string structure to bind to the HTML such as
<input id="itemname" type="text" bind-value="itemEdit.itemName">
So the Dart would be:
class ItemEdit { String itemId, String itemName, String score }
ItemEdit itemEdit = new ItemEdit();
Next we need a way to get from one to the other, so we add a method to Item
fromStrings(ItemEdit ie) {
itemid = ie.itemId == null ? null : int.parse(ie.itemId);
itemName = ie.ItemName;
score = ie.score == null ? null : double.parse(ie.score);
}
And the other way around:
toStrings(ItemEdit ie) {
ie.itemid = itemId == null ? '' : ie.itemId.toString();
ie. itemName = itemName == null ? '' : itemname; // Web_ui objects to nulls
ie.score = score == null ? null : score.toStringAsFixed(2);
}
Also, we get jason data from a database, so we need to add another method to Item:
fromJson(final String j) {
Map v = JSON.parse(j);
itemid = v['itemid'];
itemname = v['itemname'];
score = v['score'];
}
And we need to be able to revert to default values:
setDefaults() { itemId = 0; itemName = "New item"; score = 0; }
This verbosity gets me feeling like I am writing COBOL again!
There is something fundamental missing here - either in my understanding, or in the Dart/WebUI libraries.
What I would like to write is something like
class Item extends DataRecord {
int itemid = 0,
String itemName = 'New item',
double score = 0.0;
}
Then, without further coding, to be able to write code such as
item.toStrings();
...
item.fromStrings();
...
item.fromJson(json);
...
item.setDefaults(); // results in {0,'New item',0.0}
And to be able to write in the HTML:
value="{{item.strings.score}}"
If this was possible, it would be quicker, simpler, clearer, and less error prone than the code I am writing at the moment.
(Full disclosure, this answer is written with the assumption that at least one bug will be fixed. See below)
Three suggestions that might help.
Use named constructors to parse and create objects.
Take advantage of toJson() when encoding to JSON.
Use bind-value-as-number from Web UI
1) Named constructors
import 'dart:json' as json;
class Item {
int itemid;
String itemName;
double score;
Item.fromJson(String json) {
Map data = json.parse(json);
itemid = data['itemid'];
itemName = data['itemName'];
score = data['score'];
}
}
2) Encoding to JSON
The dart:json library has a stringify function to turn an object into a JSON string. If the algorithm encounters an object that is not a string, null, number, boolean, or collection of those, it will call toJson() on that object and expect something that is JSON-encodable.
class Item {
int itemid;
String itemName;
double score;
Map toJson() {
return {'itemid':itemid, 'itemName':itemName, 'score':score};
}
}
3) Now, having said that, sounds like you want to easily bind to HTML fields and get primitive values back, not just strings. Try this:
<input type="number" min="1" bind-value-as-number="myInt" />
(Note, there seems to be a bug with this functionality. See https://github.com/dart-lang/web-ui/issues/317)
(from https://groups.google.com/a/dartlang.org/forum/#!topic/web-ui/8JEAA5OxJOc)
Just found a way to perhaps help a little in the this situation:
class obj {
int gapX;
String get gapXStr => gapX.toString();
set gapXStr(String s) => gapX = int.Parse(s);
...
Now, from the HTML you can use, for example
bind-value="gapXStr"
and in code you can use
x += ob.gapX;