By using containsKey method of Map set, the object array can be de-duplicated and the field values of duplicated objects can be accumulated.

Keywords: JSON

1. Preface

A new requirement has been added to the company's project. Printing invoices adds detailed charging methods and the amount of each charge. At first, I didn't take it seriously, thinking that the payment information returned by the server contains all kinds of payment records, I would like to go through the received payment information List in the printing module and print it out.

Later, when I did it, I found that I had a simple idea.

Because the payment information returned by the server is returned according to each transaction record, that is, if the total payment is 20 yuan, and if the user pays twice 10 yuan to complete the payment, then the payment information stored by the server is two 10 yuan payment records. An example of the json string returned is as follows:

    "***INFOS\": [
        {
            \"***NAME\": \"Cash charges\",
            \"***ACCOUNT\": \"7.0\",
            \"***NUM\": \"1000000576\"
        },
        {
            \"***NAME\": \"Cash charges\",
            \"***ACCOUNT\": \"3.0\",
            \"***NUM\": \"1000000576\"
        },
        {
            \"***NAME\": \"Cash charges\",
            \"***ACCOUNT\": \"5.0\",
            \"***NUM\": \"1000000576\"
        },
        {
            \"***NAME\": \"Wechat Fees\",
            \"***ACCOUNT\": \"15.0\",
            \"***NUM\": \"1000000576\"
        },
        {
            \"***NAME\": \"Wechat Fees\",
            \"***ACCOUNT\": \"8.0\",
            \"***NUM\": \"1000000576\"
        }
    ]

As you can see, the total amount of this transaction is 38 yuan, and then the user has five payments completed. If I go through printing without doing anything, the detailed payment information displayed on the invoice information is three cash payment records and two Wechat payment records, just different in the amount of money. This is definitely not possible, so here we have to deal with the data.

2. Thoughts

In fact, the idea is very simple, that is to weigh the same payment method in the List, and then add up the amount of each payment, the total amount is calculated as the amount of money paid by this payment method. That is to say, the example transaction mentioned above should be treated as follows:

    "***INFOS\": [
        {
            \"***NAME\": \"Cash charges\",
            \"***ACCOUNT\": \"15.0\",
            \"***NUM\": \"1000000576\"
        },
        {
            \"***NAME\": \"Wechat Fees\",
            \"***ACCOUNT\": \"23.0\",
            \"***NUM\": \"1000000576\"
        }
    ]

Okay, the idea is clear, let's finish it.

3. Realization

The part of implementation is completed in the new example project.

Create an entity class object:

    public class Person {

        private String mName;
        private String mMoney;

        public Person(String pName, String pMoney) {
            mName = pName;
            mMoney = pMoney;
        }

        public String getName() {
            return mName;
        }

        public void setName(String pName) {
            mName = pName;
        }

        public String getMoney() {
            return mMoney;
        }

        public void setMoney(String pMoney) {
            mMoney = pMoney;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "mName='" + mName + '\'' +
                    ", mMoney='" + mMoney + '\'' +
                    '}';
        }
    }

Then assign the entity class to the code, and create an array of objects of the entity class, add the assigned entity class to the array:

     private ArrayList<Person> mPersons;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            Person lPerson1 = new Person("king", "100");
            Person lPerson2 = new Person("Zhang", "200");
            Person lPerson3 = new Person("plum", "300");
            Person lPerson4 = new Person("Zhang", "400");
            Person lPerson5 = new Person("king", "500");
            Person lPerson6 = new Person("king", "800");
            mPersons = new ArrayList<>();
            mPersons.add(lPerson1);
            mPersons.add(lPerson2);
            mPersons.add(lPerson3);
            mPersons.add(lPerson4);
            mPersons.add(lPerson5);
            mPersons.add(lPerson6);
        }

At this point, the initialization has been completed. As you can see, there are six pieces of data in the array, three of them are Wang, two of them are Zhang, and one of them is Li, and the amount of money each person has is different. What we need to do is to process the array after a series of processing, and only let it retain three pieces of data in the array, one surname is Wang, the number of money is 1400; one surname is Zhang, the number of money is 600; and one surname is Li, the number of money is 300.

It seems very simple, but I still take a detour in the process of practice.

3.1 detour

At first, I want to do this. First, I weigh the same family name in the array, then copy an original array, traverse the array, accumulate the money of the same family name, and then replace the data in the multiple array with the new array. This completes the work of array de-duplication and accumulation of the same field values.

It looks a bit messy, one after another to multiple arrays, one after another to accumulate arrays. So I drew a picture and looked at it and said:

Although there are more steps, there is no problem if the correct results can be obtained. (You must be smart enough to guess that in the end it didn't work, otherwise it's not called a detour.)

It is not very difficult to de-duplicate arrays. By utilizing the non-repeatability of Set sets, the de-duplication of arrays can be accomplished.

    Set<Person> lPersonSet = new HashSet<Person>();
    lPersonSet.addAll(mPersons);
    mPersons.clear();
    mPersons.addAll(lPersonSet);

It is important to note that if you only write this way, it will surely fail to repeat it. Because each person's money is different, they are not the same object. Even if the name and the amount of money are the same, the memory address of each object in the system is different, so it is not the same object, so it will fail to write.

So, here we rewrite the equals() and hashCode() methods of the Person class. In the equals() method, let it only compare names.

        @Override
        public boolean equals(Object obj) {
            if (obj == null)
                return false;

            if (this == obj)
                return true;

            if (obj instanceof Person) {
                Person lPerson = (Person) obj;
                if (lPerson.mName.equals(this.mName)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public int hashCode() {
            return mName.hashCode();
        }

In this way, it will be successful to work hard.

The problem is the sum of money. This is what I wrote:

    ArrayList<Person> mPersonsRecombine = new ArrayList<Person>();
    for (int i = 0; i < mPersons.size(); i++) {
        for (int j = 0; j < mPersons.size() - i; j++) {
            if(mPersons.get(i).getName().equals(mPersons.get(j).getName()) && i != j) {
                Person lPerson = new Person(mPersons.get(i).getName(),String.valueOf(Integer.parseInt(
    mPersons.get(i).getMoney()) + Integer.parseInt(mPersons.get(j).getMoney())));
                mPersonsRecombine.add(lPerson);
            }
        }
    }

This code can be said to be full of loopholes. First of all, although I!= J excludes the accumulation of the same item, the same item will happen twice. Secondly, more than two objects with the same surname (such as three Wang in the array) will not have enough money.

Then the train of thought stopped here, and around this issue has done a lot of repair work, are not good. I am ready to change the way to achieve, the master card is not here.

Here I would like to say that there may be a correct way to open it according to this way of thinking, but I really did not find the solution. If you have the students who have solved the problem, you must contact me and discuss it with me.

3.2 Solution

Trees move to death, people move to life.

After going through the detours described above, I'm not going to do data manipulation in ArrayList. I'm going to try putting data in a Map collection. Then I went over Map's API and found a way to do this: containsKey. This method determines whether the specified key name is included in the Map collection object. If the Map collection contains the specified key name, return true or false.

It looks like the contains method of Array List, but this method can directly determine the field name. It looks like it can be done.

    Map<String, Person> lPersonMap = new HashMap<String, Person>();
    for (Person lPerson : mPersons) {
        String personName = lPerson.getName();
        if (lPersonMap.containsKey(personName)) {
            double personMoney = Double.valueOf(lPersonMap.get(personName).getMoney());
            personMoney += Double.valueOf(lPerson.getMoney());              lPersonMap.get(personName).setMoney(Double.toString(personMoney));
        } else {
             lPersonMap.put(personName, lPerson);
        }
    }
    mPersons.clear();
    mPersons.addAll(lPersonMap.values());

The logic is very simple. First, a set of Maps is established, then the array is traversed to get the names of each field in the array. Then, the containsKey method of Map is used to determine whether the data of the current name exists in the set of Maps. If it exists, the sum of money is accumulated, and if it does not exist, the data is stored in the Map collection. Finally, the Map collection is converted into a List array.

Then, verify that:

The result proves that it is correct.

Then at this moment, I wake up to the fact that it is also possible to use ArrayList contains method, because I have rewritten the equals() and hashCode() methods of Person class. In equals(), I just need to judge the name. This is consistent with the containsKey method of Map. Of course, the containsKey method of Map is not a bit more efficient than the contains method of ArrayList.

4. Summary

Fortunately, the correct answer was found, and I came into contact with and learned a new knowledge, containsKey method. But we should also recognize our shortcomings.

First, we are stuck by the wrong way of thinking. Looking back now, we can see how complicated and complicated that detour is. In addition, we know the meaning of ArrayList contains method and rewrite the equals() and hashCode() methods of Person class, and we don't know how to combine them to change our thinking.

Second, the knowledge points are not fully grasped. If we knew the containsKey method of Map set in the early days, we would not have to spend these words and energy.

You have to practice! ~ ~

Posted by DocSeuss on Fri, 21 Jun 2019 17:37:04 -0700