0

I am getting json data as this:

[
  {
    "name": "A",
    "count": "2"
  },
  {
    "name": "B",
    "count": "1"
  },
  {
    "name": "A",
    "count": "3"
  },
  {
    "name": "C",
    "count": "10"
  },
  {
    "name": "B",
    "count": "2"
  }
]

So i iterate the data using a for loop, normal stuff. But i would like to end up with an arraylist like this:

C-->7

A-->5

B-->3

Notice that the the list is ordered from the highest to the lowest. What i have tried so far is:

//create my arraylists
    //has both name and count
     ArrayList<String>namesWithCountList = new ArrayList<>();
     //has only names 
     ArrayList<String>namesList = new ArrayList<>();

    //get json array..
    //iterate using for loop
     for (int i = 0; i < myJsonArray.length(); i++) {
      String nameStr = jsonObj.getString("name");
      int count = jsonObj.getInt("count");

      index = namesList.indexOf(name);//get index of the name

        if (index < 0) {
        // name doesnt exist in namesList, so we add it
       namesList.add(nameStr);
       namesWithCountList.add(nameStr+"-->"+count);
       }else{
        //name exists so we only add the count to existing list
       }

     }

I know i should use Comparator.sort() but i dont know how to get my list there because i have mixed both the name and count together like: C-->7

I have been stressing out on how the logic should look like, any help on this?

Avihoo Mamka
  • 4,656
  • 3
  • 31
  • 44
Steve Kamau
  • 2,755
  • 10
  • 42
  • 73

3 Answers3

6

You are getting your "mental" model wrong.

If your objects have a name and a count, then simply do not push them into strings like

"name-->count"

Instead: create your own custom class that holds these properties. And then you can create a (somehow) simpler Comparator for that class. That also simplifies the process of writing back JSON data if you intend to do that at some point.

If you insist on going on with your broken approach, then you have to create a Comparator that knows how to sort such kind of strings. In other words: your Comparator has to split such "n-->c" strings; and deduce name and count from it; to then do its job. But as said; you better give up on that idea.

As seriously: this is a super bad practice. Java is a strongly typed language; and coding multiple values into one flat string you are completely abandoning yourself from the "help" that the Java type system gives to you.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
2

Lets create an Entity class to hold your name and count mappings

import java.util.Comparator;

public class Entity implements Comparable {
private String name;
private int count;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getCount() {
    return count;
}

public void setCount(int count) {
    this.count = count;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + count;
    result = prime * result + ((name == null) ? 0 : name.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;
    Entity other = (Entity) obj;
    if (count != other.count)
        return false;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    return true;
}

public static Comparator<Entity> ArraylistComparator = new Comparator<Entity>() {

    public int compare(Entity e1, Entity e2) {
        int count1 = e1.getCount();
        int count2 = e2.getCount();

        // ascending order
        return e1.compareTo(e2);

    }
};

@Override
public int compareTo(Object o) {
    int compareCount = ((Entity) o).getCount();
    /* For Ascending order */
    return this.count - compareCount;

    /* For Descending order do like this */
    // return compareCount - this.count;
}

@Override
public String toString() {
    return "Entity [name=" + name + ", count=" + count + "]";
}

}

Then lets create a method in a separate class to populate the arrayList and call the comparator

import java.util.ArrayList;
import java.util.Collections;

public class ArraySort {

public static void main(String[] args) {
    ArrayList<Entity> arrayList = new ArrayList<Entity>();

    Entity entity1 = new Entity();
    entity1.setCount(2);
    entity1.setName("D");

    Entity entity2 = new Entity();
    entity2.setCount(10);
    entity2.setName("A");

    Entity entity3 = new Entity();
    entity3.setCount(5);
    entity3.setName("C");

    arrayList.add(entity1);
    arrayList.add(entity2);
    arrayList.add(entity3);

    System.out.println("Entity Count Sorting:");
    Collections.sort(arrayList, Entity.ArraylistComparator);

    for (Entity ent : arrayList) {
        System.out.println(ent);
    }
}

}

Output:

Entity [name=D, count=2]

Entity [name=C, count=5]

Entity [name=A, count=10]

Hope this helps!

  • Nice. I would probably mention that of course, this is just *one* way to implement that comparator. You know, you could also sort by name, and use the second information in a different way. – GhostCat Jan 12 '17 at 10:05
  • Let me check this out – Steve Kamau Jan 12 '17 at 10:07
  • @Appleman i have used your example to implement into my project, and it sorts well!! However i am unable to group similar names together and get sum of count of each group. – Steve Kamau Jan 12 '17 at 10:56
  • @GhostCat any thoughts on this? – Steve Kamau Jan 12 '17 at 10:56
  • 1
    We can do that programmatically, but I am afraid this comment section is not the best place to write the codes extensively. Suggest you create a question, which can be answered elegantly. For your trials, you need to do the following: You need two loops. The outer loop will hold a 'name' as reference. The inner loop will search for the arraylist for entries holding the same name. If you get an entry with same name, just add the 'count' fields. Then after the end of the execution of the inner loop you will have the sum of 'count's of the referenced (outer loop name's) 'name'. – Sourav Purakayastha Jan 12 '17 at 11:11
  • 1
    @SteveKamau Please understand that this SO is *not* about working through all of your problems with you. In that sense you should rather consider writing up another question; but carefully looking into the content to go there. My 2 cent: I think that *grouping* has nothing to do with the base problem. You hyve your "entity classes" as shown in the other answer ... and now you are actually looking into some "layer above" that "defines" that grouping concept. Meaning: you are again trying to "meld" things together that better stay separated! – GhostCat Jan 12 '17 at 11:41
  • @Appleman I have succesfully achieved what i wanted. But i need to know a few things first beause i am here to learn. On your `Entity.class`, there's a method you `Override hashCode()`, what is the purpose of specifically using `int prime = 31;` and `int result=1;` ? This part i haven't understood. Please shed some light on this.`code` – Steve Kamau Jan 13 '17 at 13:30
  • @GhostCat my bad, days of frustration can make a man desperate. – Steve Kamau Jan 13 '17 at 13:31
  • see http://stackoverflow.com/questions/113511/best-implementation-for-hashcode-method for example. Hint: most of the time, just turn to google and say something like "java hashCode" ... esp. when you are new with Java; it is **very** safe to assume that anything you can possible ask about ... has been asked and documented before. Many. Many. Times. ;-) – GhostCat Jan 13 '17 at 13:35
1

If you want to sum all "A", all "B" etc. then following code may help. It uses Java 8 and Lombok library (for generating boilerplate code):

@NoArgsConstructor
@AllArgsConstructor
@Data
class Entity {
    private String name;
    private int count;
}

public class Main {

    public static void main(String[] args) {
        List<Entity> list = Arrays.asList(
                new Entity("A", 2),
                new Entity("B", 1),
                new Entity("A", 3),
                new Entity("C", 10),
                new Entity("B", 2));

        Map<String, Integer> grouped =
                list.stream().collect(Collectors.groupingBy(Entity::getName, Collectors.summingInt(Entity::getCount)));
        System.out.println(grouped);
    }
}

Result:

{A=5, B=3, C=10}
Dmitry Gorkovets
  • 2,208
  • 1
  • 10
  • 19