I need to compare a current List of Objects from a DB against a new List of Objects. I want to compare them and highlight for the user those which have changed (in this case, return TRUE that they are different).
Since some of my objects are Nullable, this involves a lot of IF NOT IS Nothing on the side of the NewObj and the CurrentObj...I've trying to find a more efficient way of writing the below as I have to use it to compare about 30 objects of different types, IE Date, Decimal, Int, etc..
The below works until say either of the Obj has no Rank and is therefore Nothing
Suggestions?
Dim Rank As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim Regiment As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim DateofBirth As Boolean = CompareData(NewObj, CurrentObj, "DoB")
Private Function CompareData(NewObj As Business.Casualty, CurrentObj As Business.Casualty, FieldToComapre As String) As Boolean
Select Case FieldToComapre
Case "DateOfBirth"
Return (Nullable.Equals(NewCasualty.DateOfBirth, CurrentCasualty.DateOfBirth))
Case "Age"
Return (Nullable.Equals(NewCasualty.Age, CurrentCasualty.Age))
Case "Rank"
Return (Nullable.Equals(NewCasualty.Rank.ID, CurrentCasualty.Rank.ID))
Case "Regiment"
Return (Nullable.Equals(NewCasualty.Regiment.ID, CurrentCasualty.Regiment.ID))
Case Else
Return True
End Select
End Function
My first suggestion is to change the name of your function to something like AreFieldValuesTheSame. My second suggestion is to negate the purpose of the function. In other words, return True when the values are the same.
If you follow my first two suggestions, then for each one of your cases, instead of simply
Return (Nullable.Equals(a.ID, b.ID))
you need something more like
Return ((a Is Nothing And b Is Nothing) Or (a.ID Is Nothing And b.ID Is Nothing) Or (Nullable.Equals(a.ID, b.ID)))
For example, a represents NewCasualty.Rank and b represents CurrentCasualty.Rank. You need to check for the object being Nothing (null in other words) before you try to check properties of the object.
Also, at the beginning of the function, you need to check whether NewObj Is Nothing and whether CurrentObj Is Nothing:
If (NewObj Is Nothing) And (CurrentObj Is Nothing) Then
Return True
Else If (NewObj Is Nothing) Or (CurrentObj Is Nothing)
Return False
Please forgive me if my VB syntax is not perfect. (I'm much more fluent in C#.)
I actually ended up just going with an Extension method..it was simpler and cleaner IMO.
Dim Rank As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim Regiment As Boolean = CompareData(NewObj, CurrentObj, "Regiment")
Dim Trade As Boolean = CompareData(NewObj, CurrentObj, "Trade")
Private Function CompareData(NewObj As Business.Casualty, CurrentObj As Business.Casualty, FieldToComapre As String) As Boolean
Select Case FieldToComapre
Case "Trade"
Return NewCasualty.Trade.NullableEquals(CurrentCasualty.Trade)
Case "Rank"
Return NewCasualty.Rank.NullableEquals(CurrentCasualty.Rank)
Case "Regiment"
Return NewCasualty.Regiment.NullableEquals(CurrentCasualty.Regiment)
Case Else
Return True
End Select
End Function
public static class NullableCompare
{
public static bool NullableEquals<T>(this T s1, T s2)
where T : class
{
if (s1 == null)
{
return s2 == null;
}
return s1.Equals(s2);
}
}
public partial class Rank
{
public override bool Equals(object obj)
{
var p2 = obj as Rank;
if (p2 == null)
{
return false;
}
if (this.ID != p2.ID)
{
return false;
}
return this.ID == p2.ID;
}
public override int GetHashCode()
{
return this.ID.GetHashCode();
}
}
Related
I have an object as following :
public Class MyObjDTO {
private Long id;
private Boolean checked;
//getter and setters
#Override
public final int hashCode() {
Long id = getId();
return (id == null ? super.hashCode() : id.hashCode());
}
#Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MyObjDTO))
return false;
Long id = getId();
Long objId = ((MyObjDTO) obj).getId();
if (id.equals(objId)) {
return true;
} else {
return false;
}
}
}
And I have two hash sets containing some instances from this object :
HashSet oldSet = new HashSet();
oldSet.add(new MyObjDTO(1,true));
oldSet.add(new MyObjDTO(2,true));
oldSet.add(new MyObjDTO(3,false));
HashSet newSet = new HashSet();
newSet.add(new MyObjDTO(1,false));
newSet.add(new MyObjDTO(2,true));
newSet.add(new MyObjDTO(4,true));
So what I want to do here is to select objects that are in the newSet and not in the oldSet, in this case its : new MyObjDTO(4,true) which I did using this :
Stream<MyObjDTO> toInsert = newSet.stream().filter(e -> !oldSet.contains(e));
Then I want to select objects that are in the oldSet and not in the newSet, in this case its :new MyObjDTO(3,false) which I did using this :
Stream<MyObjDTO> toRemove = oldSet.stream().filter(e -> !newSet.contains(e));
The last step is that I want to select the objects that are in both newSet and oldSet but they have a different value for the attribute checked , in this case it's : new MyObjDTO(1,false).
What I tried is this :
Stream<MyObjDTO> toUpdate = oldSet.stream().filter(newSet::contains);
But this one will return both new MyObjDTO(1,false) and new MyObjDTO(2,true).
How can I solve this ?
One way is to first use a map and then adjust your filter condition:
Map<MyObjDTO, Boolean> map = newSet.stream()
.collect(Collectors.toMap(Function.identity(), MyObjDTO::getChecked));
Stream<MyObjDTO> toUpdate = oldSet.stream()
.filter(old -> newSet.contains(old) && old.getChecked() != map.get(old));
Firstly, your equals() and hashCode() methods violate their basic contract. As per the javadoc of hashCode():
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
Your implementation of hashCode() does not follow this contract. Your first step should be to fix that.
Secondly, since Java 1.2 (nearly 20 years ago), java has provided the method removeAll() that does exactly what you want to do for the first part:
// Given these 2 sets:
HashSet<MyObjDTO> oldSet = new HashSet<>();
HashSet<MyObjDTO> newSet = new HashSet<>();
HashSet<MyObjDTO> onlyInNew = new HashSet<>(newSet);
onlyInNew.removeAll(oldSet);
// similar for onlyInOld
For the second part, you'll need to create a Map to find and get the object out:
Map<MyObjDTO, MyObjDTO> map = new HashMap<>O;
oldSet.forEach(o -> map.put(o, o);
HashSet<MyObjDTO> updated = new HashSet<>(newSet);
updated.removeIf(o -> oldSet.contains(o) && o.getChecked()() != map.get(o).getChecked());
In the last step, you rely on the equals() method of the DTO :
Stream<FonctionnaliteDTO> toUpdate = oldSet.stream().filter(newSet::contains);
The method uses only the id field to determinate object equality.
You don't want to do that.
You want to filter on a specific field : checked.
Besides, you should perform the operation on the result of the intersection of the two Sets.
Note that you should use simply Collection.retainAll() to compute the intersection between two collections:
Set<MyObjDTO> set = ...
Set<MyObjDTO> setTwo = ...
set.retainAll(setTwo);
Then you can filter objects that have both same id and checked value by using a double loop : for + iterator.
for (MyObjDTO dto : set){
for (Iterator<MyObjDTO> it = set.iterator(); it.hasNext();){
MyObjDTO otherDto = it.next();
if (otherDto.getId().equals(dto.getId()) &&
otherDto.getChecked() == dto.getChecked()){
it.remove();
}
}
}
You could do that with Stream but IHMO it could be less readable.
I have the following function.
public String getSomething(){
TextInputDialog dialog = new TextInputDialog();
dialog.setHeaderText("X");
dialog.setTitle("Y");
Optional<String> result = dialog.showAndWait();
return result;
}
Obviously, the returned value is not of String type. How could I return a String when result's type is Optional<String>?
Try doing result.get() should return a string.
I would do something like this to make sure you are protecting from null
if(a. isPresent()) {a.get()}
I'd use orElse which provides a default value in case the actual value is absent:
public String getSomething(){
TextInputDialog dialog = new TextInputDialog();
dialog.setHeaderText("X");
dialog.setTitle("Y");
return dialog.showAndWait().orElse("n/a");
}
Use String.valueOf(optionalString);
Optional<String> s1 =Optional.of("Hello");
// s1 will print - Optional["Hello"]
String s1 = String.valueOf(optionalObj.get());
// now s1 will print "Hello"
this would extract the exact string literal from the optional object.
In a db table I have a string, such as...
Var1=0;CosType=1;DefaultType=US_Pass;DateYear=1;DateRange=1;ReportFormat=0
I want to create a VB.NET function that has 1 input var, the string (above) and the "token" to get the value for. (The return value is the value of the token.) For example, if I call it (LongString is the string above)....
txtValue.text = MyFunction(LongString,"DefaultType")
So, "US_Pass" would be returned.
What is the most efficient way to code MyFunction?
I've tried something like this...
return LongString.Substring(LongString.IndexOf(input_token) + 12)
I feel I'm close, but so far away.
Thanks!
This works as long as you know the key exists in your string:
public string MyFunction(string longString, string key)
{
return
longString
.Split(';')
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1])[key];
}
With this code:
string longString = "Var1=0;CosType=1;DefaultType=US_Pass;DateYear=1;DateRange=1;ReportFormat=0";
Console.WriteLine(MyFunction(longString, "DefaultType"));
I get:
US_Pass
As VB.NET:
Public Function MyFunction(longString As String, key As String) As String
Return longString.Split(";"c).Select(Function(x) x.Split("="c)).ToDictionary(Function(x) x(0), Function(x) x(1))(key)
End Function
Split the string into parts at the semi-colons.
Dim parts As String() = LongString.Split(";")
Loop over the parts in a ForEach loop.
Find the part that StartsWith the the token value.
Find the equal sign (IndexOf) and take everything to the right of it (Substring).
That should give you enough to figure it out.
It's probably not a great idea to store data like this in your database. Hopefully you won't need to query these attributes from SQL.
In your case I would create a class to encapsulate the attributes. You pass in the string as a constructor parameter and let the class manage it.
Here's an example in C# that shouldn't be too hard to convert to VB:
public class AttributeCollection
{
private readonly Dictionary<string, string> _attrs;
public AttributeCollection(string values)
{
_attrs = (from v in values.Split(new[] {';'})
select v.Split(new[] {'='})).ToDictionary(i => i[0], i => i[1]);
}
public string this[string name]
{
get { return _attrs[name]; }
set { _attrs[name] = value; }
}
public override string ToString()
{
return string.Join(";", (from a in _attrs select a.Key + "=" + a.Value).ToArray());
}
}
There are tables with over 50 columns, I am using following code to loop through the dataReader for column existence.
If HasColumn(reader, "EmpCode") Then obj.OwningOfficeID = CType(reader("EmpCode"), Int32)
Protected Function HasColumn(ByRef reader As SqlDataReader, ByVal columnName As String) As Boolean
For i As Integer = 0 To reader.FieldCount - 1
If reader.GetName(i).Equals(columnName) Then
Return Not IsDBNull(reader(columnName))
End If
Next
Return False
End Function
I am wondering if there is any better way of checking the columns in the DataReader instead of looping through the DataReader each time i bind the object property?
SqlDataReader.GetSchemaTable Method will give the DataTable of the executed query, from there you can get all the columns
Returns a DataTable that describes the column metadata.
MSDN http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getschematable.aspx
currently I am using this extension method for this purpose
public static bool TryGetOrdinal(this IDataRecord dr, string column, out int ordinal)
{
try
{
ordinal = dr.GetOrdinal(column);
}
catch(Exception ex)
{
ordinal = -1; //Just setting a value that GetOrdinal doesn't return
return false;
}
return true;
}
so you use it as follows
int ordinal = 0;
if(dr.TryGetOrdinal("column",out ordinal))
string val = dr.GetValue(ordinal).ToString();
My VB is not so good. Sorry couldn't convert it to VB for you. But I think you get the basic idea and can recode it in VB.
I have a method
public void DeleteItem(int ImgId, int ImgNos, int ListId)
{
object[] objParams = {0,ImgId};
SqlHelper.ExecuteNonQuery(Conn, "DeleteItem",objParams);
if (ItemNos == 2)
{
ChangeItem(ListId);
}
}
in deleteitem sp i am return the value as 0 or 1 now i want to get the return value on my btn click and want to check if return value is 0 then do some task and if return value is 1 then do some other task how to do that?
objGetBase.DeleteItem(ItemId, ItemNos, Convert.ToInt32(ViewState["ListId"]));
// here i want to check the return value if return = 0 then go to next step else not
string path1 = Path.Combine(GetDirectory(ItemName), ItemName);
File.Delete(path1);
It looks like you are using the Microsoft library with SqlHelper?
If you are things are quite simple, if you use ExecuteScalar, instead of ExecuteNonQuery you will get returned an object.
here is a link to some examples using that Libraray:
http://msdn.microsoft.com/en-us/library/ff647281.aspx
From this it should be quite straightforward:
int returnValue = (int)SqlHelper.ExecuteScalar(Conn, "DeleteItem",objParams);