I declared my Array:
Dim invoice_discountitems(100, 100) As String
Set Values into array:
For i As Int16 = 0 To data_set.Tables("discount_items").Rows.Count - 1
invoice_discountitems(i, 1) = data_set.Tables("discount_items").Rows(0).Item("item_code")
invoice_discountitems(i, 2) = data_set.Tables("discount_items").Rows(0).Item("discountitem_average")
Next
Now I try to find a single value:
Dim res As String
res = Array.IndexOf(invoice_discountitems, "FO1506")
MsgBox(res)
But, I get this error :(
"Only single dimension arrays are supported here"
This is a fundamentally wrong approach - for a number of reasons
You're treating ALL the data points as Strings
You're not taking advantage of DB optimisations like indices
You're loading data into memory that you're never going to use (at least int he example)
The Nicest way to do it would be with Linq-To-Entities:
Dim Record = MyDBContext.Discount_Items.Where(function(x) x.ItemCode = "FO1506").Single
Console.WriteLine(Record.discountitem_average);
If you're stuck with your current Data Access Layer, you need to modify the SQL being executed to only return the information you're interested in. Without more information, I can't provide decent example code but you want the SQL to end up looking like this...
SELECT itemcode,
discountitem_average,
[Other fields],
FROM MyDatabase.M
EDIT: To Clarify, there are a number of ways to access data in a database. The one I prefer is LINQ-To-Entities (Have a look through this tutorial).
In short, you add a new Item to your project and point it at your database. This becomes your "Database Context" - it represents the database and that's how you run queries.
Project -> Add -> New Item...
Select ADO.Net Entity Data Model (Linq-To-Entities is almost Identical to Linq-To-Sql but more recent and better supported - use Entities until you know the difference)
Call it something like MyDBContext
When prompted, choose "Generate From Database" and point it at your database.
It's worth noting that the designer takes advantage of information in the database like Foreign Key Constraints - So the better your database is designed, the better the model it will create.
Then, you refer to it in code as shown in my first example.
First of all IndexOf return int as index!
To get the index of string
Try:
Dim i As int
Dim j As int
i = Array.IndexOf(invoice_discountitems.OfType(Of String)().ToArray(), "FO1506")
j = i MOD 100
i= i/100
MsgBox(i+" "+j)
(I use c# but I think it's not different)
Related
Context
I have a interface in VB.NET that extract the data from the UniVerse using UniObjects for .NET
Problem
From the COB file I need to get all keys where the FEC.COB field is equal to a specific date and the field SEC is equal to 04.
An expert in UniVerse Database told me that I can run the follow queries:
SELECT COB WITH FEC.COB > “31/10/2013”
SELECT.ID 1 2 04
But I don't know how can I do that with UniObjects library. Can anyone help me?
I don't use UniObjects as my shop normally gets data our of UniVerse via ODBC. Also my VB is bad, so I don't have much metacode for you, but the basic idea would be to do something like this.
1.) Create a UV Session. Hopefully you have that much worked out as I can be of next to no help there.
2.) Once the session is established Execute your query by doing something like this
session.Command.Text = "SELECT COB WITH FEC.COB > '31/10/2013'"
session.Command.Exec
(I converted your double quotes to single quotes and Universe won't mind).
3.) If you just need the IDs, you can get them by iterating through the select list that your query returns. A command line query will always return to list 0 unless you specify otherwise in your UV query. In most cases your results will be in session.SelectList(0)
Dim objSelect As object
Set objSelect = objSession.SelectList(0)
4.) It looks like the SelectList object has a ReadList method which returns a Dynamic Array Object, which you should be able to iterate through using normal array looping. Additionally you can use a while loop and next to do what you need to do.
Dim someObject as Object
someObject = objSelect.Next ' Get first ID
Do While Not objSelect.LastRecordRead
' Do something here with someObject. Maybe ToString it or something
someObject = objSelect.Next' Get next ID
Loop
Hope that is somewhat helpful.
I'm just looking to be able to sort the results of a BatchedJoinBlock (http://msdn.microsoft.com/en-us/library/hh194683.aspx) so that the different results of the different targets stay together. I will explain! Example in some pseudo-code:
Dim batchedJoin = New BatchedJoinBlock(Of String, object)(4)
batchedJoin.Target1.Post("String1Target1")
batchedJoin.Target2.Post(CType(BuildIt, StringBuilder1))
batchedJoin.Target1.Post("String1Target2")
batchedJoin.Target2.Post(CType(BuildIt, StringBuilder2))
Dim results = batchedJoin.Receive()
'This sorts one result...
Dim SortByResult = results.Item1.OrderBy(Function(item) item.ToString, New NaturalStringComparer)
Basically I've got a string and an object, the SortByResult variable above sorts the strings exactly as I'd like them to sort. I'm looking for a way to get the objects that used to be at the same index number in target2 into the same order. e.g. if "String1Target1" changes order I'd like to somehow reliably refer to/pair it together with "StringBuilder1". The actual end result just needs to be that the objects (target2) are sorted in the order that is dictated by the strings being sorted (target1). Something like:
Dim EndResult = results.Item2.OrderBy(strings in target1)
but I'll gladly take an intermediate solution! I've also tried using a dictionary (results.Item2.ToDictionary) with the string as a key (which would also be a fine solution) but it's a bit beyond my ken using lamba expressions in the proper context. I can realistically do this in several steps with a list or something, but I'm trying to get something more efficient/learn something, and it seems like there's a lot of default options with the results of the jointblock that I'm just not experienced enough to use. Thanks in advance for any help you can provide!
To me, it looks like you don't actually want BatchedJoinBlock, because the two pieces of data always come together. A better option for that would be a BatchBlock of Tuple<string, object>. When you have that, you can then use LINQ directly to sort each batch:
results.OrderBy(Function(tuple) tuple.Item1)
This is an ugly one. I wish I wasn't having to ask this question, but the project is already built such that we are handling heavy loads of validations in the database. Essentially, I'm trying to build a function that will take two stacks of data, weave them together with an unknown batch of operations or comparators, and produce a long string.
Yes, that was phrased very poorly, so I'm going to give an example. I have a form that can have multiple iterations of itself. For some reason, the system wants to know if the entered start date on any of these forms is equal to the entered end date on any of these forms. Unfortunately, due to the way the system is designed, everything is stored as a string, so I have to format it as a date first, before I can compare. Below is pseudo code, so please don't correct me on my syntax
Input data:
'logFormValidation("to_date(#) == to_date(^)"
, formname.control1name, formname.control2name)'
Now, as I mentioned, there are multiple iterations of this form, and I need to loop build a fully recursive comparison (note: it may not always be typical boolean comparisons, it could be internally called functions as well, so .In or anything like that won't work.) In the end, I need to get it into a format like below so the validation parser can read it.
OR(to_date(formname.control1name.1) == to_date(formname.control2name.1)
,to_date(formname.control1name.2) == to_date(formname.control2name.1)
,to_date(formname.control1name.3) == to_date(formname.control2name.1)
,to_date(formname.control1name.1) == to_date(formname.control2name.2)
:
:
,to_date(formname.control1name.n) == to_date(formname.control2name.n))
Yeah, it's ugly...but given the way our validation parser works, I don't have much of a choice. Any input on how this might be accomplished? I'm hoping for something more efficient than a double recursive loop, but don't have any ideas beyond that
Okay, seeing as my question is apparently terribly unclear, I'm going to add some more info. I don't know what comparison I will be performing on the items, I'm just trying to reformat the data into something useable for ANY given function. If I were to do this outside the database, it'd look something like this. Note: Pseudocode. '#' is the place marker in a function for vals1, '^' is a place marker for vals2.
function dynamicRecursiveValidation(string functionStr, strArray vals1, strArray vals2){
string finalFunction = "OR("
foreach(i in vals1){
foreach(j in vals2){
finalFunction += functionStr.replace('#', i).replace('^', j) + ",";
}
}
finalFunction.substring(0, finalFunction.length - 1); //to remove last comma
finalFunction += ")";
return finalFunction;
}
That is all I'm trying to accomplish. Take any given comparator and two arrays, and create a string that contains every possible combination. Given the substitution characters I listed above, below is a list of possible added operations
# > ^
to_date(#) == to_date(^)
someFunction(#, ^)
# * 2 - 3 <= ^ / 4
All I'm trying to do is produce the string that I will later execute, and I'm trying to do it without having to kill the server in a recursive loop
I don't have a solution code for this but you can algorithmically do the following
Create a temp table (start_date, end_date, formid) and populate it with every date from any existing form
Get the start_date from the form and simply:
SELECT end_date, form_id FROM temp_table WHERE end_date = <start date to check>
For the reverse
SELECT start_date, form_id FROM temp_table WHERE start_date = <end date to check>
If the database is available why not let it do all the heavy lifting.
I ended up performing a cross product of the data, and looping through the results. It wasn't the sort of solution I really wanted, but it worked.
Given a datatable containing two columns like this:
Private Function CreateDataTable() As DataTable
Dim customerTable As New DataTable("Customers")
customerTable.Columns.Add(New DataColumn("Id", GetType(System.Int32)))
customerTable.Columns.Add(New DataColumn("Name", GetType(System.String)))
Dim row1 = customerTable.NewRow()
row1.Item("Id") = 1
row1.Item("Name") = "Customer 1"
customerTable.Rows.Add(row1)
Dim row2 = customerTable.NewRow()
row2.Item("Id") = 2
row2.Item("Name") = "Customer 2"
customerTable.Rows.Add(row2)
Dim row3 = customerTable.NewRow()
row3.Item("Id") = 3
row3.Item("Name") = "Customer 3"
customerTable.Rows.Add(row3)
Return customerTable
End Function
Would you use this snippet to retrieve a List(Of Integer) containing all Id's:
Dim table = CreateDataTable()
Dim list1 As New List(Of Integer)
For i As Integer = 0 To table.Rows.Count - 1
list1.Add(CType(table.Rows(i)("Id"), Integer))
Next
Or rather this one:
Dim list2 = (From r In table.AsEnumerable _
Select r.Field(Of Integer)("Id")).ToList()
This is not a question about whether to type cast the Id column to Integer by using .Field(Of Integer), CType, CInt, DirectCast or whatever but generally about whether or not you choose Linq over forloops as the subject implies.
For those who are interested: I ran some iterations with both versions which resulted in the following performance graph:
graph http://dnlmpq.blu.livefilestore.com/y1pOeqhqQ5neNRMs8YpLRlb_l8IS_sQYswJkg17q8i1K3SjTjgsE4O97Re_idshf2BxhpGdgHTD2aWNKjyVKWrQmB0J1FffQoWh/analysis.png?psid=1
The vertical axis shows the milliseconds it took the code to convert the rows' ids into a generic list with the number of rows shown on the horizontal axis. The blue line resulted from the imperative approach (forloop), the red line from the declarative code (linq).
Whatever way you generally choose: Why do you go that way and not the other?
Whenever possible I favor the declarative way of programming instead of imperative. When you use a declarative approach the CLR can optimize the code based on the characteristics of the machine. For example if it has multiple cores it could parallelize the execution while if you use an imperative for loop you are basically locking this possibility. Today maybe there's no big difference but I think that in the future more and more extensions like PLINQ will appear allowing better optimization.
I avoid linq unless it helps readability a lot, because it completely destroys edit-and-continue.
When they fix that, I will probably start using it more, because I do like the syntax a lot for some things.
For almost everything I've done I've come to the conclusion that LINQ is optimized enough. If I handcrafted a for loop it would have better performance, but in the grand scheme of things we are usually talking milliseconds. Since I rarely have a situation where those milliseconds will make any kind of impact, I find it's much more important to have readable code with clear intentions. I would much rather have a call that is 50ms slower than have someone come along and break it altogether!
Resharper has a cool feature that will flag and convert loops into Linq expressions. I will flip it to the Linq version and see if that hurts or helps readability. If the Linq expression more clearly communicates the intent of the code, I will go with that. If the Linq expression is unreadable, I will flip back to the foreach version.
Most of the performance issues don't really compare with readability for me.
Clarity trumps cleverness.
In the above example, I would go with the the Linq version since it clearly explains the intent and also locks out people accidently adding side effects in the loop.
I recently found myself wondering whether I've been totally spoiled by LINQ. Yes, I now use it all the time to pick all sort of things out from all sort of collections.
I started to, but found out in some cases, I saved time by using this approach:
for (var i = 0, len = list.Count; i < len; i++) { .. }
Not necessarily in all cases, but some. Most extension methods use the foreach approach of querying.
I try to follow these rules:
Whenever I'm just querying (filtering, projecting, ...) collections, use LINQ.
As soon as I'm actually 'doing' something with the result (i.e, introduce side effects), I'll use a for loop.
So in this example, I'll use LINQ.
Also, I always try to split up the 'query definition' from the 'query evaluation':
Dim query = From r In table.AsEnumerable()
Select r.Field(Of Integer)("Id")
Dim result = query.ToList()
This makes it clear when that (in this case in-memory) query will be evaluated.
I want to update a custom user field in QC using the Label of field instead of the name
At the moment we are doing it this way
Set currentRun = QCUtil.CurrentRun
currentRun.Field("RN_USER_03") = 1
currentRun.Post
But I would like to do it this way
Set currentRun = QCUtil.CurrentRun
currentRun.Field("Data Rows Passed") = 4
currentRun.Post
But I can't find the method to do it with.
Any Ideas?
Implying all labels are unique (which I doubt..):
You could create a function which accepts a label, searches in QC's tables that define customized fields for the correct field definition, and returns the field name. Then use the function's result value as the indexed property's index.
Suppose that function would be called "GetNameOfLabel", then the Caller's code would look like:
Set currentRun = QCUtil.CurrentRun
currentRun.Field(GetNameOfLabel ("Data Rows Passed")) = 1
currentRun.Post
Of course, the function would not really be trivial, but easy enough after some digging in the QC data model and finding an efficient way to fetch the name from the DB via SQL.
Or, the function could look up the name in an array, or a dictionary, then you would have to maintain that dictionary, but you would not have to go to the database for each lookup.
Disadventages:
Scripts with the wrong label might be harder to be debug
If labels are not unique, it might be real "fun" to debug
If looking up on the DB:
All scripts slow down if you don't cache, or pre-load, SQL query results for those lookups;
complexity, as you have to do the right SQL query, and you depend on QC's data model in a quite peculiar way (usually a horror when you're upgrading)
If looking up in an array, or dictionary:
You either must maintain its initialization (bet other admin guys adding a cust field will forget that easily), or must "load" it from QC's table (which is a bit like the SQL solution above, and has the same downsides).
I'd go with the array/dictionary-initialized-from-db-idea. Or, if you can live with the constant idea already presented, that one is a good bet. Considering that there is no session-independent scope in QCs customizing scripts, the SQL access idea might really kill performance because it would have to be executed for every new user session. Which is why I, too, +1'd the constant idea.
Look at this:
Dim gFieldLabelToNameDICT: Set gFieldLabelToNameDICT = CreateObject("Scripting.Dictionary")
gFieldLabelToNameDICT.CompareMode = vbTextCompare
Function GetNameOfLabel (strFieldLabel)
' If it doesn't exist yet in fieldLabelToName dict -> search it using TDC and add it to the list to improve performance
If Not gFieldLabelToNameDICT.Exists(strFieldLabel) Then
Dim testSetFields As List
Dim testSetFields: Set testSetFields = QCUtil.QCConnection.Customization.Fields.Fields("RUN")
For Each aField in testSetFields
If aField.UserLabel = strFieldLabel Then
gFieldLabelToNameDICT.Item(strFieldLabel) = aField.ColumnName
End If
Next aField
End If
GetNameOfLabel = gFieldLabelToNameDICT.Item(strFieldLabel)
End Function
Maybe you shall want to add some more error handling, such us considering the case that the label is not found.