I'm doing Join algorithm in MapReduce. In the Map phase, I made joinColumn as key and the tuple as value. In the reduce method, I have keys and values as (columnname, row). In the reduce phase, I need to separate the "row" into two based on which table they belong to.
I used MultiMap to do this. But the MultiMap is overwriting the existing value. To try to overcome this, I override "equals" and "hashcode" but this did not fix the problem.
public void reduce(Text key,Iterable<Text> values,Context context) throws IOException, InterruptedException{
Multimap<String,Table> entry=LinkedListMultimap.create();
for(Text val : values){
String[] row=val.toString().split(",");
Table t = new Table();
t.setTablename(row[0]);
t.setColumns(val);
entry.put(row[0],t);
}
for (String k: entry.keySet()){
System.out.println("Key : "+k);
Collection<Table> rows=entry.get(k);
Iterator<Table> i=rows.iterator();
while(i.hasNext()){
Table t=i.next();
System.out.println(t.getColumns());
}
}
public class Table {
private String tablename;
private Text columns;
public String getTablename() {
return tablename;
}
public void setTablename(String tablename) {
this.tablename = tablename;
}
public Text getColumns() {
return columns;
}
public void setColumns(Text columns) {
this.columns = columns;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((columns == null) ? 0 : columns.hashCode());
result = prime * result
+ ((tablename == null) ? 0 : tablename.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Table other = (Table) obj;
if (columns == null) {
if (other.columns != null)
return false;
} else if (!columns.equals(other.columns))
return false;
if (tablename == null) {
if (other.tablename != null)
return false;
} else if (!tablename.equals(other.tablename))
return false;
return true;
}
}
I'm getting the following output:
Key : S
R, 2, Don, Larson, Newark, 555-3221
R, 2, Don, Larson, Newark, 555-3221
Key : R
R, 2, Don, Larson, Newark, 555-3221
Key : S
R, 3, Sal, Maglite, Nutley, 555-6905
R, 3, Sal, Maglite, Nutley, 555-6905
Key : R
R, 3, Sal, Maglite, Nutley, 555-6905
Key : R
S, 4, 22000, 7000, part1
Key : S
S, 4, 22000, 7000, part1
It is overriding the existing values. Can anyone help me to sort out this problem?
Your problem is that the object returned by iterating over values is reused by the iterator. Instead of just assigning the value in setColumns(), you need to copy it. Something like:
public void setColumns(Text columns) {
this.columns = new Text(columns.toString());
}
Related
I have the below code that is creating the PriortyQueue structure using Dart. But since I cannot use heapify function inside the Constructor or factory constructor I cannot initialize PQ with an existing set of List. Can somebody guide me and show me how I can use heapify while creating PQ instance so I can initialize it with an existing List? Also If you have any other suggestions against doing something like this please also help me as well. thank you
class PriorityQueue<T extends Comparable<T>> {
List<T?> _tree;
PriorityQueue._(List<T?> tree) : _tree = tree;
factory PriorityQueue([List<T>? array]) {
List<T?> newArray = [null, ...array ?? []];
// ignore: todo
//TODO: missing heapify
return PriorityQueue._(newArray);
}
void insert(T node) {
_tree.add(node);
_swim(_tree.length - 1);
}
T getTop() {
_swap(1, _tree.length - 1);
T top = _tree.removeLast() as T;
_sink(1);
return top;
}
List<T> _heapify(List<T> array) {
int sinkNodeIndex = (array.length - 1) ~/ 2;
while (sinkNodeIndex >= 1) {
_sink(sinkNodeIndex);
sinkNodeIndex--;
}
}
void _sink(int nodeIndex) {
int leftChildIndex = nodeIndex * 2;
int rightChildIndex = leftChildIndex + 1;
int minNodeIndex = leftChildIndex;
// index can be unreachable
T? leftChild =
leftChildIndex >= _tree.length ? null : _tree[leftChildIndex];
T? rightChild =
rightChildIndex >= _tree.length ? null : _tree[rightChildIndex];
if (leftChild == null) {
return;
}
if (rightChild != null && leftChild.compareTo(rightChild) > 0) {
minNodeIndex = rightChildIndex;
}
if ((_tree[minNodeIndex] as T).compareTo(_tree[nodeIndex] as T) < 0) {
_swap(nodeIndex, minNodeIndex);
_sink(minNodeIndex);
}
}
void _swim(int nodeIndex) {
if (nodeIndex <= 1) return;
int parentIndex = nodeIndex ~/ 2;
if ((_tree[nodeIndex] as T).compareTo(_tree[parentIndex] as T) < 0) {
_swap(nodeIndex, parentIndex);
_swim(parentIndex);
}
}
void _swap(int i, int j) {
T temp = _tree[i] as T;
_tree[i] = _tree[j];
_tree[j] = temp;
}
#override
String toString() {
return _tree.toString();
}
}
I would make all the helper functions. _heapify, _sink/_swim, even _swap, be static functions which take the list as argument.
Then you can use them from anywhere, including inside the factory constructor.
Alternatively, you can change the constructor to returning:
return PriorityQueue._(newArray).._heapify();
This creates the PriorityQueue object, and then calls the _heapify method on it, before returning the value.
(I'd also make _tree have type List<T> and not insert the extra null at the beginning. It's more efficient to add/subtract 1 from indices than it is to cast to T.)
I ended up doing like Irn's first suggestion. But when I do functions static they lost Type of the class so I needed to specify for each function. Also, making List<T?> instead of List ended up with me fighting against the compiler.
class PriorityQueue<T extends Comparable<T>> {
List<T?> _tree;
PriorityQueue._(List<T?> tree) : _tree = tree;
factory PriorityQueue([List<T>? array]) {
List<T?> newArray = [null, ...array ?? []];
_heapify(newArray);
return PriorityQueue._(newArray);
}
bool get isNotEmpty {
return _tree.isNotEmpty;
}
void insert(T node) {
_tree.add(node);
_swim(_tree, _tree.length - 1);
}
void insertMultiple(List<T> array) {
for (var element in array) {
insert(element);
}
}
T? removeTop() {
if (_tree.length == 1) return null;
_swap(_tree, 1, _tree.length - 1);
T top = _tree.removeLast() as T;
_sink(_tree, 1);
return top;
}
void removeAll() {
_tree = [null];
}
static void _heapify<T extends Comparable<T>>(List<T?> array) {
int sinkNodeIndex = (array.length - 1) ~/ 2;
while (sinkNodeIndex >= 1) {
_sink(array, sinkNodeIndex);
sinkNodeIndex--;
}
}
static void _sink<T extends Comparable<T>>(List<T?> tree, int nodeIndex) {
int leftChildIndex = nodeIndex * 2;
int rightChildIndex = leftChildIndex + 1;
int minNodeIndex = leftChildIndex;
T? leftChild = leftChildIndex >= tree.length ? null : tree[leftChildIndex];
T? rightChild =
rightChildIndex >= tree.length ? null : tree[rightChildIndex];
if (leftChild == null) {
return;
}
if (rightChild != null && leftChild.compareTo(rightChild) > 0) {
minNodeIndex = rightChildIndex;
}
if ((tree[minNodeIndex] as T).compareTo(tree[nodeIndex] as T) < 0) {
_swap(tree, nodeIndex, minNodeIndex);
_sink(tree, minNodeIndex);
}
}
static void _swim<T extends Comparable<T>>(List<T?> tree, int nodeIndex) {
if (nodeIndex <= 1) return;
int parentIndex = nodeIndex ~/ 2;
if ((tree[nodeIndex] as T).compareTo(tree[parentIndex] as T) < 0) {
_swap(tree, nodeIndex, parentIndex);
_swim(tree, parentIndex);
}
}
static void _swap<T extends Comparable<T>>(List<T?> tree, int i, int j) {
T temp = tree[i] as T;
tree[i] = tree[j];
tree[j] = temp;
}
#override
String toString() {
return _tree.toString();
}
}
I try a small code but I have a strange behavior that I can't explain.
I want according to a value to return the "keyvalue" of a map which is based on the key.
My code works with positive value.
If the value is not in the array then it returns null.
It also works with negative values only if the value is included in my array.
If I put a negative value lower than my array then it returns not null but zero which is false!
Keys in my map must be String.
My code that you can test on dartPad :
import 'dart:collection';
void main() {
int myVar = -360;
Map<String, dynamic> values = {
"-200" : 42,
"-100" : 21,
"0" : 0,
"100" : -22,
"150" : -30,
"200" : -43,
"300" : -64
};
Map<String, dynamic> filter(int myVar, Map<String, dynamic> values) {
SplayTreeMap<String, dynamic> newval = SplayTreeMap.of(values);
String convertString = myVar.toString();
if (values.containsKey(convertString)) {
return {convertString: values[convertString]};
}
String lowerKey;
String upperKey;
if(myVar > 0){
lowerKey = newval.lastKeyBefore(convertString);
upperKey = newval.firstKeyAfter(convertString);
}
else{
lowerKey = newval.firstKeyAfter(convertString);
upperKey = newval.lastKeyBefore(convertString);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
var result = filter(myVar, values);
print('============================');
print(result);
}
First I want to give a minor complain about the use of dynamic in the code. It is totally fine to use dynamic in cases where the type cannot be determined on runtime like JSON parsing. But in this case, all the types can be determined and the use of dynamic is not necessary. So I have fixed the code to remove the usage of dynamic and also removed unnecessary typing:
import 'dart:collection';
void main() {
const myVar = -360;
final values = {
"-200": 42,
"-100": 21,
"0": 0,
"100": -22,
"150": -30,
"200": -43,
"300": -64
};
Map<String, int> filter(int myVar, Map<String, int> values) {
final newVal = SplayTreeMap.of(values);
final convertString = myVar.toString();
if (values.containsKey(convertString)) {
return {convertString: values[convertString]};
}
String lowerKey;
String upperKey;
if (myVar > 0) {
lowerKey = newVal.lastKeyBefore(convertString);
upperKey = newVal.firstKeyAfter(convertString);
} else {
lowerKey = newVal.firstKeyAfter(convertString);
upperKey = newVal.lastKeyBefore(convertString);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
final result = filter(myVar, values);
print('============================');
print(result);
}
Your problem is that you are using SplayTreeMap to sort your keys in values but you have used Strings to represent your numbers. This is rather confusing since numbers is valid keys. But this also means that your sorting in your SplayTreeMap is alphabetical and not by number. This is properly the reason why your code does not work as expected.
You can either change the type of your keys to int or provide a compare method to your SplayTreeMap which changes how the sorting are done.
I have made the following example where I have changed the type of keys into int which makes your code work:
import 'dart:collection';
void main() {
const myVar = -360;
final values = {
-200: 42,
-100: 21,
0: 0,
100: -22,
150: -30,
200: -43,
300: -64
};
Map<int, int> filter(int myVar, Map<int, int> values) {
final newVal = SplayTreeMap.of(values);
if (values.containsKey(myVar)) {
return {myVar: values[myVar]};
}
int lowerKey;
int upperKey;
if (myVar > 0) {
lowerKey = newVal.lastKeyBefore(myVar);
upperKey = newVal.firstKeyAfter(myVar);
} else {
lowerKey = newVal.firstKeyAfter(myVar);
upperKey = newVal.lastKeyBefore(myVar);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
final result = filter(myVar, values);
print('============================');
print(result);
}
Output
-200
null
============================
{-200: 42}
The following code illustrates a logic I need in a Spring Reactive project:
Inputs:
var period = 3;
int [] inArr = {2, 4, 6, 7, 9, 11, 13, 16, 17, 18, 20, 22 };
Calculation:
var upbond = inArr[0] + period;
var count =0;
List<Integer> result = new ArrayList();
for(int a: inArr){
if(a <= upbond){
count++;
}else{
result.add(count);
count = 1;
upbond += period;
}
}
result.add(count);
System.out.println(Arrays.toString(result.toArray()));
The data source of the sorted integers is the Flux from DB where it shall continually fetch data once a new suitable data is written into the DB. And the result shall be a stream that is sending out to another node through RSocket (by the request-stream communication mode).
After some online searching on Reactor, including some tutorials, I still can't figure out how to write the logic in the Flux fashion. The difficulty I have is that those calculations on data defined outside of the loop.
How shall I approach it in the Reactor?
The scan() variant that lets you use a separately typed accumulator is your friend here.
I'd approach this with a separate State class:
public class State {
private int count;
private Optional<Integer> upbond;
private Optional<Integer> result;
public State() {
this.count = 0;
this.upbond = Optional.empty();
this.result = Optional.empty();
}
public State(int count, int upbond) {
this.count = count;
this.upbond = Optional.of(upbond);
this.result = Optional.empty();
}
public State(int count, int upbond, int result) {
this.count = count;
this.upbond = Optional.of(upbond);
this.result = Optional.of(result);
}
public int getCount() {
return count;
}
public Optional<Integer> getUpbond() {
return upbond;
}
public Optional<Integer> getResult() {
return result;
}
}
...and then use scan() to build up the state element by element:
sourceFlux
.concatWithValues(0)
.scan(new State(), (state, a) ->
a <= state.getUpbond().orElse(a + period) ?
new State(state.getCount() + 1, state.getUpbond().orElse(a + period)) :
new State(1, state.getUpbond().orElse(a + period) + period, state.getCount())
)
.windowUntil(s -> s.getResult().isPresent())
.flatMap(f -> f.reduce((s1, s2) -> s1.getResult().isPresent()?s1:s2).map(s -> s.getResult().orElse(s.getCount() - 1)))
Aside: The concatWithValues() / windowUntil() / flatMap() bits are there to handle the last element - there's probably a cleaner way of achieving that, if I think of it I'll edit the answer.
I think scan is definitely the right tool here, combined with a stateful class, although my approach would be slightly different than Michaels.
Accumulator:
class UpbondAccumulator{
final Integer period;
Integer upbond;
Integer count;
Boolean first;
Queue<Integer> results;
UpbondAccumulator(Integer period){
this.period = period;
this.count = 0;
this.upbond = 0;
this.results = new ConcurrentLinkedQueue<>();
this.first = true;
}
//Logic is inside accumulator, since accumulator is the only the only thing
//that needs it. Allows reuse of accumulator w/o code repetition
public UpbondAccumulator process(Integer in){
//If impossible value
//Add current count to queue and return
//You will have to determine what is impossible
//Since we concat this value on the end of flux
//It will signify the end of processing
//And emit the last count
if(in<0){
results.add(count);
return this;
}
//If first value
//Do stuff outside loop
if(this.first) {
upbond = in + period;
first=false;
}
//Same as your loop
if(in <= upbond)
count++;
else {
results.add(count);
count = 1;
upbond += period;
}
//Return accumulator
//This could be put elsewhere since it isn't
//Immediately obvious that `process` should return
//the object but is simpler for example
return this;
}
public Mono<Integer> getResult() {
//Return mono empty if queue is empty
//Otherwise return queued result
return Mono.justOrEmpty(results.poll());
}
}
Usage:
dbFlux
//Concat with impossible value
.concatWithValues(-1)
//Create accumulator, process value and return
.scan(new UpbondAccumulator(period), UpbondAccumulator::process)
//Get results, note if there are no results, this will be empty
//meaning it isn't passed on in chain
.flatMap(UpbondAccumulator::getResult)
Following comment from Michael here is an immutable approach
Accumulator:
public class UpbondAccumulator{
public static UpbondState process(int period,Integer in,UpbondState previous){
Integer upbond = previous.getUpbond().orElse(in + period);
int count = previous.getCount();
if(in<0) return new UpbondState(upbond, count, count);
if(in <= upbond) return new UpbondState(upbond,count + 1 , null);
return new UpbondState(upbond + period, 1, count);
}
}
State object:
public class UpbondState {
private final Integer upbond;
private final int count;
private final Integer result;
public UpbondState() {
this.count = 0;
this.upbond = null;
this.result = null;
}
public UpbondState(Integer upbond, int count,Integer result) {
this.upbond = upbond;
this.count = count;
this.result = result;
}
public int getCount() { return count; }
public Optional<Integer> getUpbond() { return Optional.ofNullable(upbond); }
public Integer getResult() { return result; }
public boolean hasResult() { return result!=null; }
}
Usage:
dbFlux
.concatWithValues(-1)
.scan(new UpbondState(),
(prev, in) -> UpbondAccumulator.process(period,in,prev))
//Could be switched for Optional, but would mean one more map
//+ I personally think makes logic less clear in this scenario
.filter(UpbondState::hasResult)
.map(UpbondState::getResult)
I am trying to persist the following class in a Neo4J database using spring-data-neo4j version 4.0.0.RELEASE. It is a class named 'GroupCategory' with some fields like name, ownerId etc. It has overwritten the equals and hashcode methods as provided by eclipse framework.
#NodeEntity(label="GroupCategory")
public class GroupCategory implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#GraphId
private Long id;
#Property private String uuid;
#Property private String name;
#Property private String creatorUuid;
#Property private String ownerUuid;
#Property private String profile;
#Property private String status;
#Relationship(type = GroupRelationshipNames.BELONGS_TO, direction = Relationship.INCOMING)
private List<GroupCategoryRelation> groupCategoryRelations = new ArrayList<GroupCategoryRelation>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCreatorUuid() {
return creatorUuid;
}
public void setCreatorUuid(String creatorUuid) {
this.creatorUuid = creatorUuid;
}
public String getOwnerUuid() {
return ownerUuid;
}
public void setOwnerUuid(String ownerUuid) {
this.ownerUuid = ownerUuid;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<GroupCategoryRelation> getGroupCategoryRelations() {
return groupCategoryRelations;
}
public void setGroupCategoryRelations(
List<GroupCategoryRelation> groupCategoryRelations) {
this.groupCategoryRelations = groupCategoryRelations;
}
#Override
public String toString() {
return "GroupCategory [id=" + id + ", uuid=" + uuid + ", name=" + name
+ ", creatorUuid=" + creatorUuid + ", ownerUuid=" + ownerUuid
+ ", profile=" + profile + ", status=" + status
+ ", groupCategoryRelations=" + groupCategoryRelations + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((creatorUuid == null) ? 0 : creatorUuid.hashCode());
result = prime
* result
+ ((groupCategoryRelations == null) ? 0
: groupCategoryRelations.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result
+ ((ownerUuid == null) ? 0 : ownerUuid.hashCode());
result = prime * result + ((profile == null) ? 0 : profile.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GroupCategory other = (GroupCategory) obj;
if (creatorUuid == null) {
if (other.creatorUuid != null)
return false;
} else if (!creatorUuid.equals(other.creatorUuid))
return false;
if (groupCategoryRelations == null) {
if (other.groupCategoryRelations != null)
return false;
} else if (!groupCategoryRelations.equals(other.groupCategoryRelations))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (ownerUuid == null) {
if (other.ownerUuid != null)
return false;
} else if (!ownerUuid.equals(other.ownerUuid))
return false;
if (profile == null) {
if (other.profile != null)
return false;
} else if (!profile.equals(other.profile))
return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
if (uuid == null) {
if (other.uuid != null)
return false;
} else if (!uuid.equals(other.uuid))
return false;
return true;
}
}
I have a test case which first saves and later updates some properties of the objects like below. The test case is as follows
#Test
public void changeCategoryName(){
String ownerUuid = "PERSON_0_UUID";
String name = "GROUP_0";
String name1 = "GROUP_1";
GroupCategory groupCategory = new GroupCategory();
groupCategory.setName(name);
groupCategory.setOwnerUuid(ownerUuid);
groupCategory.setProfile(Profile.PRIVATE.name());
GroupCategory savedGroupCategory = groupCategoryService.create(groupCategory);
System.out.println("----------- "+groupCategoryService.findByUuid(savedGroupCategory.getUuid()));
Assert.assertTrue(groupCategoryService.findByUuid(savedGroupCategory.getUuid()).getName().equals(name));
savedGroupCategory.setName(name1);
savedGroupCategory = groupCategoryService.save(savedGroupCategory);
System.out.println("----------- "+groupCategoryService.findByUuid(savedGroupCategory.getUuid()));
Assert.assertTrue(groupCategoryService.findByUuid(savedGroupCategory.getUuid()).getName().equals(name1));
savedGroupCategory.setName(name);
groupCategoryService.save(savedGroupCategory);
System.out.println("----------- "+groupCategoryService.findByUuid(savedGroupCategory.getUuid()));
Assert.assertTrue(groupCategoryService.findByUuid(savedGroupCategory.getUuid()).getName().equals(name));
}
The answer of the three 'printlns' are given below
----------- GroupCategory [id=889, uuid=9f891006-3d89-4665-ae2f-4946d13b74ac, name=GROUP_0, creatorUuid=null, ownerUuid=PERSON_0_UUID, profile=PRIVATE, status=ACTIVE, groupCategoryRelations=[]]
----------- GroupCategory [id=889, uuid=9f891006-3d89-4665-ae2f-4946d13b74ac, name=GROUP_1, creatorUuid=null, ownerUuid=PERSON_0_UUID, profile=PRIVATE, status=ACTIVE, groupCategoryRelations=[]]
----------- GroupCategory [id=889, uuid=9f891006-3d89-4665-ae2f-4946d13b74ac, name=GROUP_1, creatorUuid=null, ownerUuid=PERSON_0_UUID, profile=PRIVATE, status=ACTIVE, groupCategoryRelations=[]]
If the 'name' field is looked at, it canbe observed that the field is saved and updated for the first two cases. But in the third case, while resetting to the previous value of 'Group_0', the name field is not saved.
The last assertion in the test case fails because the saved object retains the previous value of 'Group1'. The three printlns will give the idea.
It so happens, that the transition of the value of name field from Group_0 to Group_1 works but the one from Group_1 back to Group_0 does not. If the value is changed to any other value other than Group_0 from Group_1, then the value gets updated. That is, the save is not working if the value is being changed alternatively
If I remove the hashCode() method from the GroupCategory class, then all seems to work well. Specifically only commenting the hash code for name field does the trick like
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((creatorUuid == null) ? 0 : creatorUuid.hashCode());
result = prime
* result
+ ((groupCategoryRelations == null) ? 0
: groupCategoryRelations.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
//result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result
+ ((ownerUuid == null) ? 0 : ownerUuid.hashCode());
result = prime * result + ((profile == null) ? 0 : profile.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
But the same problem persits for other fields as well. As for now it seems that, updating values alternatively (value1 to value2, then again to value1) does not seem to work if hashCode() is being overridden in the class
This bug has been fixed, please use version 1.1.6 of neo4j-ogm.
Update
SDN depends on the Neo4j OGM library. SDN 4.0 was released with an older version of Neo4j-OGM that does not contain this bug fix.
You can add this dependency to override the version of Neo4j OGM-
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm</artifactId>
<version>1.1.6</version>
</dependency>
What is the most efficient way to retrieve distinct column values. I have a table with fields FormsID and ProductLineDescription along with other columns. ProductLine table has a one to many relationship, the sample data in the table would be like:
FormID ProdculineDesc
1 abc
2 abc
1 xyz
2 def
3 abc
3 xyz
I want the dropdown to have just the distinct values of ProductLineDesc. Here is the code,
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid).ToSelectList().Distinct();
}
This still gives me every ProductLineDesc, how do I retrieve just the Distinct values.
Here's a version with a custom quality comparer (with example):
public class UniqueProductLineDesc : IEqualityComparer<Product>
{
public Boolean Equals(Product a, Product b)
{
if (Object.ReferenceEquals(a, b))
return true;
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(b, null))
return false;
return String.Compare(a.ProductLineDesc, b.ProductLineDesc, true) == 0;
}
public Int32 GetHashCode(Product product)
{
if (Object.ReferenceEquals(product, null))
return 0;
return product.ProductLineDesc == null ? 0 : product.ProductLineDesc.GetHashCode();
}
}
And to implement:
private void LoadProductLines(Models.SearchModel Model, xyzEntities Context)
{
Model.ProductLine = Context.PRODUCTLINEs
.OrderBy(T => T.FormsGuid)
.ToSelectList()
.Distinct(new UniqueProductLineDesc());
}
How about something like:
var distinctInfo = (from c in Context.PRODUCTLINEs
group by c.ProductLineDesc into result
select new Model.ProductLine {
ProductLineDesc = result.Key,
FormID = result.Min(d=>d.FormID)
});