In my reduce method, I want to operate with the TreeMap variable reduceMap to aggregate the incoming key values. However, this map loses it's state with every reduce method call. Subsequently Hadoop prints only the very last value (plus the test values I added) that is put into the TreeMap. Why is that? It does work as I intend it in my map method.
public static class TopReducer extends
Reducer<Text, IntWritable, Text, IntWritable> {
private TreeMap<Text, Integer> reducedMap = new TreeMap<Text, Integer>();
#Override
public void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
String strValues = "";
for (IntWritable value : values) {
sum += value.get();
strValues += value.get() + ", ";
}
System.out.println("Map size Before: " +reducedMap);
Integer val = sum;
if (reducedMap.containsKey(key))
val += reducedMap.get(key);
// Only add, if value is of top 30.
reducedMap.put(key, val);
System.out.println("Map size After: " +reducedMap);
reducedMap.put(new Text("test"), 77777);
System.out.println("REDUCER: rcv: (" + key + "), " + "(" + sum
+ "), (" + strValues + "):: new (" + val + ")");
}
/**
* Flush top 30 context to the next phase.
*/
#Override
protected void cleanup(Context context) throws IOException,
InterruptedException {
System.out.println("-----FLUSHING TOP " + TOP_N
+ " MAPPING RESULTS-------");
System.out.println("MapSize: " + reducedMap);
int i = 0;
for (Entry<Text, Integer> entry : entriesSortedByValues(reducedMap)) {
System.out.println("key " + entry.getKey() + ", value "
+ entry.getValue());
context.write(entry.getKey(), new IntWritable(entry.getValue()));
if (i >= TOP_N)
break;
else
i++;
}
}
}
Hadoop re-uses object references for efficiency purposes - so when you call reducedMap.put(key, val) the key value will match a key already in the map (because Hadoop had just replaced the contents of your key object, not given you a new reference to a new object with new contents). It's effectively the same as calling the following:
Text key = new Text("x");
reducedMap.put(key, val); // map will be of size 1
key.set("y");
reducedMap.put(key, val); // map will still be of size 1
// as it will be comparing key to the itself
// and just updating the mapped value val
You need to make a deep copy of your key before putting it into the map:
reducedMap.put(new Text(key), val)
Related
I've a custom Entry Renderer in Android and I've implemented BeforeTextChanged event so that I get the text which is gonna be entered, but it doesn't return that text.
I've implemented in two different ways:
First implementing ITextWatcher by the class and then overriding the mentioned event (helping this link):
void ITextWatcher.BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
var test1 = new string(s?.ToArray());
var test2 = Control?.Text;
SpannableString spannableString = new SpannableString(s);
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.Blue);
spannableString.SetSpan(backgroundSpan, start, start + count,
SpanTypes.ExclusiveExclusive);
var test3 = spannableString;
}
Second way:
Control.BeforeTextChanged += (sender, args) =>
{
var test1 = new string(args?.Text.ToArray());
var entry = sender as EditText;
var test2 = entry?.Text;
var test3 = Control?.Text;
};
But none of them will return the text that is about to be entered.
What I want is to access that text and only in some circumstances allowing it to be inserted.
I don't wanna use Behavior as it doesn't suit my need.
First way, ITextWatcher:
class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer() : base()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
Control.AddTextChangedListener(new MyTextWatcher());
}
}
}
class MyTextWatcher :Java.Lang.Object, ITextWatcher
{
public void AfterTextChanged(IEditable s)
{
Android.Util.Log.Error("lv", "afterTextChanged ---->" + s);
}
public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
Android.Util.Log.Error("lv", "beforeTextChanged ----> s=" + s + "----start=" + start
+ "----after=" + after + "----count" + count);
}
public void OnTextChanged(ICharSequence s, int start, int before, int count)
{
Android.Util.Log.Error("lv", "onTextChanged ---->s=" + s + "----start=" + start
+ "----before=" + before + "----count" + count);
}
}
it works well.
Second way, Control.BeforeTextChanged, it also works well.
it doesn't return that text
The methods you provided in your question, they are all work well in my phone. Please show more codes, so I can help you find where is wrong.
I'm trying to write down something more sophisticated than simple Fahrenheit to Celsius and vice versa converter. I'm trying to use JoptionPane for better fell but I'm stuck in one place and have no idea how to resolve this (line 32 and 37 - method not applicable for the arguments ()) any help will be appreciated.
import java.text.DecimalFormat;
import javax.swing.JOptionPane;
public class ex74v3 {
public static void main(String[] args) {
temp();
new ex74v3();
}
public ex74v3() {
boolean done=false;
while(!done){
done=true;
String[] ans=new String[11];
String[] choice={
"(°F) to (°C)",
"(°C) to (°F)",
};
int choice_indx=JOptionPane.showOptionDialog(null, "Choose type of conversion", "Choice",
0,JOptionPane.QUESTION_MESSAGE ,
null,choice,0);
ans[0]=choice[choice_indx];
if(choice_indx==1 || choice_indx==2) {
done=false;
}else{
choice_indx=JOptionPane.showMessageDialog(null, "Fahrenheit to Celsius: " + baseFtC() + " (°C)");
}
if(choice_indx==2) {
done=false;
}else{
choice_indx=JOptionPane.showMessageDialog(null, "Celsius to Fahrenheit : " + baseCtF() + " (°F)");
}
}
}
public static int temp() {
String value = JOptionPane.showInputDialog(null, "Enter value ");
#SuppressWarnings("unused")
int log;
return log = Integer.parseInt(value);
}
public int baseCtF(int value) {
int conversion = (int) (temp() * 1.8 + 32);
return conversion;
}
public int baseFtC(int value) {
int conversion = (int) ((temp() - 32) / 1.8);
return conversion;
}
}
Ok, there is an other way, easier, thanks anyway ;]
import java.text.DecimalFormat;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class ex74v4 {
public static void main(String[] a) {
DecimalFormat digit = new DecimalFormat("0.00");
String value = JOptionPane.showInputDialog(null, "Enter value ");
float log;
log = Float.parseFloat(value);
JFrame frame = new JFrame();
Object stringArray[] = { "Celsius to Fahrenheit", "Fahrenheit to Celsius" };
int reply = JOptionPane.showOptionDialog(frame, "Choose conversion type of value: " + digit.format(log), "MiniConverter",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, stringArray,
stringArray[0]);
if (reply == JOptionPane.YES_OPTION) {
DecimalFormat digit2 = new DecimalFormat("0.00");
double conversion = log * 1.8 + 32;
JOptionPane.showMessageDialog(frame, log + " " + "(°C) equals " + digit2.format(conversion) + " (°F)", "MiniConverter", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
else {
DecimalFormat digit3 = new DecimalFormat("0.00");
double conversion = (log - 32) / 1.8;
JOptionPane.showMessageDialog(frame, log + " " + "(°F) equals " + digit3.format(conversion) + " (°C)", "MiniConverter", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
In need to emit two keys and two values from my mapper. could you please provide me info , how to write code and data type for that. for example :-
key = { store_id : this.store_id,
product_id : this.product_id };
value = { quantity : this.quantity,
price : this.price,
count : this.count };
emit(key, value);
regards
As per the given example, A B B C A R A D S D A C A R S D F A B
From the mapper emit
key - A
value A, AB
key - B
value B,BB
key - B
value B, BC
key - C
value C, CA
and so on...
In the reducer, you get the grouped values
key - A
values A, AB, A, AR, A, AD, A, AC and so on
key - B
value - B, BB,B,BC and so on
Add a delimiter of your choice between the 2 words/alphabets
for each key in reducer, you can use a hashmap/mapwritable to track the occurrence count of each value
ie for example
A - 5 times
AB - 7 times
etc etc
Then you can calculate the ratio
Sample Mapper Implementation
public class TestMapper extends Mapper<LongWritable, Text, Text, Text> {
#Override
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] valueSplits = value.toString().split(" ");
for(int i=0;i<valueSplits.length;i++){
if(i!=valueSplits.length-1){
context.write(new Text(valueSplits[i]),new Text(valueSplits[i]+"~"+valueSplits[i+1]));
}
context.write(new Text(valueSplits[i]), new Text(valueSplits[i]));
}
}
}
Sample Reducer Implementation
public class TestReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Map<String,Integer> countMap= new HashMap<String,Integer>();
for(Text t : values){
String value = t.toString();
int count =0;
if(countMap.containsKey(value)){
count = countMap.get(value);
count+=1;
}else{
count =1;
}
countMap.put(value, count);
}
for(String s : countMap.keySet()){
if(s.equalsIgnoreCase(key.toString())){
}else{
int keyCount = countMap.get(s.split("~")[0]);
int occurrence = countMap.get(s);
context.write(new Text(key.toString()+" , "+s), new Text(String.valueOf((float)occurrence/(float)keyCount)));
}
}
}
}
For an input of
A A A B
the reducer would emit
A , A~A 0.6666667
A , A~B 0.33333334
AA appears 2 times, AB 1 time and A 3 times.
AA is hence 2/3
AB is hence 1/3
Any ideas on how to apply Collections.sort method to sort my arraylist by priority of each grocItem object within the itemData ArrayList?
public class GroceryProgram {
private final static int GROC_SIZE = 6;
private final List<ItemData> itemData = new ArrayList<ItemData>();
private void setUpList() {
Scanner keyboard = new Scanner(System.in);
for (int i = 0; i < GROC_SIZE; i++) {
System.out.print("\nEnter item name (" + i + ") : ");
String name = keyboard.next();
System.out.print("\nEnter the price of item (" + i + ") : ");
double cost = keyboard.nextDouble();
System.out.print("\nEnter Priority Number (" + i + ") : ");
int priority = keyboard.nextInt();
ItemData grocItem = new ItemData(name, cost, priority);
itemData.add(grocItem); // add grocery items to itemData ArrayList
Collections.sort(grocItem);
for (Int priority : priority) {
System.out.println(integer);
Call sort() with a Comparator. For example, a Comparator in ascending-order of priority could look like this.
Collections.sort( items, new Comparator<ItemData>() {
public int compare (ItemData o1, ItemData o2) {
int comp = o1.getPriority() - o2.getPriority();
return comp;
}
});
PS: 'itemData' is bad variable naming -- it would refer to a single item, not a list. 'groceryItems', 'stockItems' or 'itemList' would be better.
Variable names should enable you to speak in meaningful, clear, concise English about your software.
Hope this helps.
Could you use Collections.sort(List list, Comparator c) to deal with this? You could simply do the following :
Collections.sort(itemData, new Comparator() {
public int compare(Object o1, Object o2) {
// Return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
}
}
You can use custom comparator as below:
Collections.sort(itemData ,new PriorityComparator());
//print sorted arraylist,it will print the data in ascending order (low priority->high priority).feel free to modify if you want to go for descending order
System.out.println(itemData );
PriorityComparator class definition:
private static class PriorityComparator implements Comparator{
#Override
public int compare(ItemData object1, ItemData object2) {
return (object1.priority< object2.priority) ? -1: (object1.priority> object2.priority) ? 1:0 ;
}
}
Reference:
how-to-sort-arraylist-in-java-example
I hope it will be helpful !!
Hadoop Version: 0.20.2 (On Amazon EMR)
Problem: I have a custom key that i write during map phase which i added below. During the reduce call, I do some simple aggregation on values for a given key. Issue I am facing is that during the iteration of values in reduce call, my key got changed and i got values of that new key.
My key type:
class MyKey implements WritableComparable<MyKey>, Serializable {
private MyEnum type; //MyEnum is a simple enumeration.
private TreeMap<String, String> subKeys;
MyKey() {} //for hadoop
public MyKey(MyEnum t, Map<String, String> sK) { type = t; subKeys = new TreeMap(sk); }
public void readFields(DataInput in) throws IOException {
Text typeT = new Text();
typeT.readFields(in);
this.type = MyEnum.valueOf(typeT.toString());
subKeys.clear();
int i = WritableUtils.readVInt(in);
while ( 0 != i-- ) {
Text keyText = new Text();
keyText.readFields(in);
Text valueText = new Text();
valueText.readFields(in);
subKeys.put(keyText.toString(), valueText.toString());
}
}
public void write(DataOutput out) throws IOException {
new Text(type.name()).write(out);
WritableUtils.writeVInt(out, subKeys.size());
for (Entry<String, String> each: subKeys.entrySet()) {
new Text(each.getKey()).write(out);
new Text(each.getValue()).write(out);
}
}
public int compareTo(MyKey o) {
if (o == null) {
return 1;
}
int typeComparison = this.type.compareTo(o.type);
if (typeComparison == 0) {
if (this.subKeys.equals(o.subKeys)) {
return 0;
}
int x = this.subKeys.hashCode() - o.subKeys.hashCode();
return (x != 0 ? x : -1);
}
return typeComparison;
}
}
Is there anything wrong with this implementation of key? Following is the code where I am facing the mixup of keys in reduce call:
reduce(MyKey k, Iterable<MyValue> values, Context context) {
Iterator<MyValue> iterator = values.iterator();
int sum = 0;
while(iterator.hasNext()) {
MyValue value = iterator.next();
//when i come here in the 2nd iteration, if i print k, it is different from what it was in iteration 1.
sum += value.getResult();
}
//write sum to context
}
Any help in this would be greatly appreciated.
This is expected behavior (with the new API at least).
When the next method for the underlying iterator of the values Iterable is called, the next key/value pair is read from the sorted mapper / combiner output, and checked that the key is still part of the same group as the previous key.
Because hadoop re-uses the objects passed to the reduce method (just calling the readFields method of the same object) the underlying contents of the Key parameter 'k' will change with each iteration of the values Iterable.