基本问题是,如何将DataTable中单行中的多个连续列引用为可以使用For Next结构处理的二维数组?背景是:
有问题的程序从.csv文件加载数据,其中每行包含一个人的基本身份信息,然后是他们对20多个问题的数字答案。程序循环浏览.csv文件的每一行,并识别与当前行具有最高精确答案匹配数的其他五行。
DataTable似乎是读取.csv文件的最佳结构,但不确定如何将每行的最后x列引用为表单答案(人,问题)的数组。
如果这看起来真的很容易或完全不切实际,我应该做以下免责声明:程序代码已经写好并可以工作,但我正在把它从QuickBASIC 4(是的,我确实说过QB4…)重新编码到VB.NET。这个程序基本上是一个约会程序,在过去20年左右的时间里,我每年都会运行一次,当地的学校会把火柴作为筹款活动出售。已经到了Windows 7和Windows XP的最新补丁版本都无法运行QB4的地步,所以我下载了桌面版VS Express,并以此为机会学习VB.NET。我已经做了很多(非窗口的)VBScript应用程序脚本,但对VB6的一些真正轻松的涉猎是我使用传统VB的唯一体验。这里的每个人都很清楚,.NET中的文件I/O与VB6或以前的版本非常不同。这就是我现在要对抗的…
回答Zohar的问题/评论:
下面是.csv文件格式的示例。实际的文件有几百行长,但格式完全相同。为了隐私起见,姓名和电话号码都被更改了。字段按顺序为:
姓氏
名字
电话号码(如果给定,则为占位符)
性别(1=M;2=F)
问题1(1-4)的答案
问题2(1-4)的答案
……
问题24(1-4)的答案
Mouse,Mickey,xxx-xxxx,1,2,3,3,2,3,1,3,4,2,1,4,3,1,1,2,1,2,1,1,1,2,1,1,4
Mouse,Minnie,555-9931,2,1,3,1,2,1,2,3,3,3,4,4,2,4,1,2,3,4,4,4,1,2,1,1,4
Duck,Donald,555-7024,1,2,3,4,2,4,3,4,2,2,1,4,2,4,1,2,1,1,2,1,3,2,1,1,1
McDuck,Scrooge,555-4824,1,2,3,3,2,1,2,4,3,2,4,4,2,4,1,4,2,2,4,4,3,2,1,1,4
GoodWitch,Wendy,xxx-xxxx,2,2,2,4,2,1,2,4,4,3,4,2,2,1,1,2,1,1,4,4,4,4,1,3,1
之所以使用二维数组,是为了创建一个按用户和问题编号列出答案的单一变量数据库。请参阅下面的实际现有QB4代码的排序部分。下面的二维数组是StudentAnswer(matchFrom,question)。
For matchFrom = 1 To numberSheets
'
'The following section of code finds the top maximumToMatch groups of n
'matching questions per sheet
'
For x = 1 To maximumToMatch
topMatches(x) = 0
sheetsMatched(x) = 0
Next x
For matchTo = 1 To numberSheets
If StudentSex(matchFrom) <> StudentSex(matchTo) Then
numberMatched(matchTo) = 0
highMatch = 0
For question = 1 To numberQuestions
If StudentAnswer(matchFrom, question) = StudentAnswer(matchTo, question) Then
numberMatched(matchTo) = numberMatched(matchTo) + 1
End If
Next question
If numberMatched(matchTo) = topMatches(1) Then
sheetsMatched(1) = sheetsMatched(1) + 1
End If
If numberMatched(matchTo) > topMatches(1) Then
match = maximumToMatch
done = False
Do
If numberMatched(matchTo) = topMatches(match) Then
sheetsMatched(match) = sheetsMatched(match) + 1
done = True
End If
If numberMatched(matchTo) > topMatches(match) Then
For x = 1 To match - 1
topMatches(x) = topMatches(x + 1)
sheetsMatched(x) = sheetsMatched(x + 1)
Next x
topMatches(match) = numberMatched(matchTo)
sheetsMatched(match) = 1
done = True
Else
match = match - 1
End If
Loop Until done
End If
Else
numberMatched(matchTo) = 0
End If
Next matchTo
...
<additional code to narrow it down to a fixed number of sheet matches>
Next matchFrom
预测另外两个可能的问题:
现有的代码是为了使M与F匹配而编写的,反之亦然。我想在重写时让它更灵活,但这是一个农村地区,我真的不确定他们是否准备好了。。。
数据文件采用.csv格式的原因是缺少为程序编写的正式数据输入前端。它一直在待办事项列表中,但与此同时,由于它每年只运行一次,Excel一直是我的朋友。。。如果一切顺利,我将在VB.NET重写期间设计一个数据输入屏幕。
提前感谢所有花时间阅读本文的人。
既然LINQ可以查询对象列表(因为.NET 3.5,所以不是新的),使用DataTable
可能不是最好的选择,因为数据来自CSV文件而不是数据库(LINQ也可以用于数据库)。
所以,这并不是你最初问题的答案,但如果你输入了这样的东西:
Joe User,1,2,3,4,5
Jane User,2,2,3,4,6
Jack User,3,4,5,2,8
Jill User,5,3,1,8,6
您可以定义一个类来存储数据:
Public Class UserInfo
Public Property Name As String
Public Property Answers As List(Of Integer) = New List(Of Integer)()
Public Function MatchRating(other As UserInfo) As Integer
Dim rating As Integer = 0
For i = 0 To Me.Answers.Count - 1
If Me.Answers(i) = other.Answers(i) Then
rating += 1
End If
Next
Return rating
End Function
End Class
然后,您可以将CSV数据读取到UserInfo
对象列表中:
Dim users = File.ReadLines("Data.csv").Select(
Function(line)
Dim parts = line.Split(","c)
Dim user = New UserInfo() With {.Name = parts(0)}
user.Answers.AddRange(parts.Skip(1).Select(Function(str) CInt(str)))
Return user
End Function
).ToList()
然后,您可以用这样的方法找到最佳匹配,该方法在用户中循环,并使用LINQ查询根据匹配的答案数量找到前5个匹配(UserInfo.MatchRating
函数),跳过任何不匹配的答案(rating > 0
):
For Each user In users
Console.WriteLine("{0}:{1}", user.Name, String.Join(",", user.Answers))
Dim bestMatches = From u In users
Where u IsNot user
Let rating = u.MatchRating(user)
Where rating > 0
Order By rating Descending
Take 5
Select New With {.Name = u.Name, .Rating = rating}
For Each match In bestMatches
Console.WriteLine(" Match: {0}, rating: {1}", match.Name, match.Rating)
Next
Next
您需要为UserInfo
类添加用于实际身份信息的属性,并调整代码以匹配。
您还需要确保您的项目选项设置正确,并且需要适当的导入/引用,例如:
Option Explicit On
Option Infer On
Option Strict On
Imports System.IO
作为参考,我的测试结果是(可怜的杰克,我想你可能需要根据性偏好进行调整,例如Where u IsNot user AndAlso u.Sex <> user.Sex
):
Joe User:1,2,3,4,5
Match: Jane User, rating: 3
Jane User:2,2,3,4,6
Match: Joe User, rating: 3
Match: Jill User, rating: 1
Jack User:3,4,5,2,8
Jill User:5,3,1,8,6
Match: Jane User, rating: 1