Java Streams - Map two string lines each to one object [duplicate] - java

This question already has answers here:
Collect successive pairs from a stream
(22 answers)
Closed 1 year ago.
Say we have a text file that contains (product name, price) pairs. Each pair occupies two lines in the text file where the first line corresponds to the product name, and the second line corresponds to the price of that product. We may assume the text file is in the right format (and has an even amount of lines)
Example:
Ice Cream
$3.99
Chocolate
$5.00
Nice Shoes
$84.95
...
Now I have a simple class representing such pairs:
public class Product {
private final String name;
private final int price;
public Product(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return this.name;
}
public int getPrice() {
return this.price;
}
}
We read the file containing the pairs and now have a string array containing all the individual lines. I need to use Streams to map each two lines to one object of type Product.
How can I group two lines each and then map them to a Product? If there is a simple approach, does it still work with parallel streams?

You can make your own Collector which temporarily stores the previous element/string. When the current element starts with a $, the name of the product is stored in prev. Now you can convert the price to a double and create the object.
private class ProductCollector {
private final List<Product> list = new ArrayList<>();
private String prev;
public void accept(String str) {
if (prev != null && str.startsWith("$")) {
double price = Double.parseDouble(str.substring(1));
list.add(new Product(prev, price));
}
prev = str;
}
public List<Product> finish() {
return list;
}
public static Collector<String, ?, List<Product>> collector() {
return Collector.of(ProductCollector::new, ProductCollector::accept, (a, b) -> a, ProductCollector::finish);
}
}
Since you need to rely on the sequence (line with price follows line with name), the stream cannot be processed in parallel. Here is how you can use your custom collector:
String[] lines = new String[]{
"Ice Cream", "$3.99",
"Chocolate", "$5.00",
"Nice Shoes", "$84.95"
};
List<Product> products = Stream.of(lines)
.sequential()
.collect(ProductCollector.collector());
Note that your prices are not integers which is why I used a double to represent them properly.

if you have the items in an array you can use vanilla java with an intStream and filter on even values and then in the next map you can use index and index+1. Maybe have look here

Related

Given a Map with values of type string, which are all numbers separated by commas, would it be possible to transform each value into a double?

I recently asked about converting Json using Gson into something I can sort values into, and the best option was using a Linked HashMap.
List<String> stringList = Arrays.asList(tm.split(" |,")); // split into pair key : value
Map<String, List<String>> mapString = new LinkedHashMap<>();
stringList.forEach(s1 -> {
String[] splitedStrings = s1.split(": "); //split into key : value
String key = splitedStrings[0].replaceAll("[^A-Za-z0-9]",""); // remove non alphanumeric from key, like {
String value = splitedStrings[1];
if (mapString.get(key) == null) {
List<String> values = new ArrayList<>();
values.add(value);
mapString.put(key, values);
}else if (mapString.get(key) != null) {
mapString.get(key).add(value);
}
});
When this code is run, a map with keys for frequency, magnitude, and other attributes of my data is created. This is the original Json Message compared to the resulting map value for the same set of data (Formatted to make it easier to understand and look better)
{"groupId":"id3_x_","timestamp":1.591712740507E9,"tones":
[{"frequency":1.074,"level":3.455,"bw":0.34,"snr":3.94,"median":0.877},
{"frequency":14.453,"level":2.656,"bw":0.391,"snr":2.324,"median":1.143},
{"frequency":24.902,"level":0.269,"bw":0.282,"snr":2.216,"median":0.121},
{"frequency":22.607,"level":0.375,"bw":0.424,"snr":2.034,"median":0.184},
{"frequency":9.863,"level":2.642,"bw":0.423,"snr":1.92,"median":1.376}]}
To Map values:
Message Received
Group ID: id3_x_
Message Topic: pi7/digest/tonals
Time of Arrival: 1.591712740507E9
---------------DATA---------------
Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
Magnitude: [3.46, 2.66, 0.27, 0.38, 2.64]
Bandwidth: [0.34, 0.39, 0.28, 0.42, 0.42]
SNR: [3.94, 2.32, 2.22, 2.03, 1.92]
Median: [0.88, 1.14, 0.12, 0.18, 1.38]]
While this is very useful for analyzing the data, the information stored is a string. What I would like to be able to do is transform each of the values in the map (Example: Frequency 1.07, 14.45, etc.) into doubles that i can then run through additional programs and run calculations with, such as an average. I have looked around online and havnt found anything that I am looking for, so im wondering if there would be a way to transform these strings into doubles using either an array, list, or any other means.
I am an intern for a tech company so I am still trying to hammer in Java and describing what I am talking about, so if there is any questions about what I am asking, please let me know and thanks in advance!
You could get a Map from the JSON file , you can also extract the values array from the Map yourmap.getvalues() , then you can parse each on of these element and case it into double
Example : Frequency: [1.07, 14.45, 24.90, 22.61, 9.86]
for ( String f : Frequency ) {
double f_double = Double.parse(f); // turns String into double
}
You can do this with another class that will store duplicate attribute values in arrays. You can simply get them through a.getValues (). This is just a concept and you should extend it as it will be convenient for you.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, List<Attribute>> map = new LinkedHashMap<>();
List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute("frequency", 3.46, 5.11, 6.12));
attributes.add(new Attribute("magnitude", 3.46, 10.22, 10.54));
//and so on
map.put("idString1", attributes);
//printing double values
for (String key : map.keySet()) {
for (Attribute a : map.get(key)) {
System.out.println(a.getName() + " " +Arrays.toString(a.getValues()));
//a.getValues() to get all of doubles
}
}
}
private static class Attribute {
private String name;
private double[] values;
Attribute(String name, double... values) {
this.name = name;
this.values = values;
}
String getName() {
return name;
}
double[] getValues() {
return values;
}
}
}
The result will be:
frequency [3.46, 5.11, 6.12]
magnitude [3.46, 10.22, 10.54]
Your question:
I would like to be able to do is transform each of the String values in the
map (Example: Frequency 1.07, 14.45, etc.) into doubles and run calculations with, such as an average.
Yes, it is possible to transform your String array in a double array using Stream like below:
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
double[] arr = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.toArray();
If you use the DoubleSummaryStatistics class you have already available ops like avg, sum :
String[] frequencies = { "1.07", "14.45", "24.90", "22.61", "9.86" };
DoubleSummaryStatistics statistics = Stream.of(frequencies)
.mapToDouble(Double::parseDouble)
.summaryStatistics();
System.out.println(statistics.getAverage()); //<-- 14.578
System.out.println(statistics.getMax()); //<-- 24.9

Is it possible in Java to add one value of an Enum to an Arraylist a number of times based on another value in the Enum?

public enum Fruits{
APPLE(1, 1,"Apple"),
ORANGE(2, 4, "Orange"),
PEAR(3, 7, "Pear");
private int fruitId, amount;
private String name;
private Fruits(int fruitId, int amount, String name) {
this.fruitId= fruitId;
this.amount= amount;
this.name= name;
}
}
Basically I am wondering if it is possible to create an Array list of the "fruitId" value for each Enum entry, adding the fruitId to the list equal to the amount number. For example, for the ORANGE it would the number 2 to the list 4 times. If it is possible, how can this be done?
You can accomplish this with a simple, nested for-loop that first iterates over the value of the Fruits enum, and then over each value's amount:
List<Integer> list = new ArrayList<>();
for (Fruits fruit : Fruits.values()) {
for (int i = 0; i < fruit.getAmount(); i++) {
list.add(fruit.getFruitId());
}
}
This assumes that you have created the valid getter methods for each field.
A stream approach would look like the following:
Arrays.stream(Fruits.values())
.flatMap(fruit -> Stream.generate(() -> fruit.getFruitId()).limit(fruit.getAmount()))
.collect(Collectors.toList())

How to start on this Priority Queue homework?

Could someone give me hints on how to solve this problem?
A store announces a sale campaign whereby any customer who buys two items gets 50% off on the cheaper one. If the customer buys more than two items, he/she must group them into pairs of two to indicate the items that the oer should apply to.
Suppose you want to buy n items in total. Write a method that will give you the best pairing of the items (the one with the minimum price). The method's signature is:
public static LinkedList<ItemPair> minPairing(LinkedList<Item> items).
If you leave it up to the store owner, he/she will try to pair the items in order to obtain the maximum price. Write a method that will help the store owner achieve this. The method's signature is:
public static LinkedList<ItemPair> maxPairing(LinkedList<Item> items).
How much will you gain if you use your method (instead of the shop owner's method) for the following list of item prices: 60 $, 100 $, 400 $, 600 $, 200 $, 80 $.
Using the following classes:
public class Item {
private int id;
private double price;
public Item (int id, double price ) {
this.id = id;
this. price = price;
}
int getId () {
return id;
}
double getPrice () {
return price;
}
}
and the class :
public class ItemPair {
public Item first;
public Item second;
public ItemPair (Item first, Item second ) {
this. first = first;
this. second = second;
}
}
Edited to add PriorityQueue as requested.
For minPairing() copy the items into a new PriorityQueue<Item> with a Comparator<Item> which compares the items prices. Then always pair (ItemPair) the top ones from the queue using .poll(). Repeat until list ist empty.
To create the PriorityQueue try this:
PriorityQueue<Item> pq = new PriorityQueue<Item>(items.size(), new Comparator<Item>() {
public int compare(Item i1, Item i2) {
return Float.compare(i1.getPrice(), i2.getPrice());
});
for (Item item : items) {
pq.add(item);
}
For maxPairing() - in my opinion - a PriorityQueue doesn't make much sense, as you need to pair the most expensive one with the cheapest one. I would just go with a sorted List<Item> here (see Collections.sort(List, Comparator)). Pick the first and the last item from the list and create an ItemPair. Then remove both from the list. Repeat until list ist empty.
compare the sum of the prices of the ItemPairs provided by those two methods.
You may need to implement a getPrice() method to ItemPair:
double getPrice () {
// assumes that second is cheaper one:
return first.getPrice() + (second.getPrice() / 2);
}

Associative arrays in Java

I'm from a PHP background, and I'm trying to create a multidimentional array with a difficulty in understanding the Java way of doing things. I thought this could be achieved using JSON and the GSON library, but I'm failing to understand how this is done having followed several tutorials online.
Here is what I'm after in PHP, how can I achieve the same thing in Java?
function creatCars($id) {
$aCars = array(
0 => array(
'name' => 'vauxhall',
'doors' => 5,
'color' => 'black',
),
1 => array(
'name' => 'peogeot',
'doors' => 3,
'color' => 'red',
),
);
return $aCars[$id];
}
function printFirstCarName($sName) {
$aCar = createCars(0);
echo $aCars['name'];
}
//prints "vauxhall"
printFirstCarName();
Arrays in PHP are not the same as arrays in Java. Here are the differences:
PHP:
PHP arrays are actually dictionaries. They store a value for each key, where a key can be an integer or a string. If you try to use something else as a key, it will be converted to either an integer or a string.
Java:
Arrays in Java
Java arrays are not associative in the same way as they are in PHP. Let's start with one-dimensional arrays in Java:
A one-dimensional array in Java has a fixed length (that cannot be changed) and each key is an integer in the range of 0 to array.length - 1. So keys, actually called indexes, are always integers. Also, in Java, if you have an array with the keys 2 and 4, you also have (at least) the keys 0, 1 and 3, because the length has to be at least 5 then.
Arrays in Java also have exactly one type and each values in the array can only be of the specified type. Neither size nor type of an array can be changed.
When you create an array in Java, you have two possibilities:
explicitly specify the length when creating the array
String[] words = new String[4];
The variable words now holds an array of type String with the length a length of 4. The values of all indexes (0 to 3) are initially set to null.
specify elements when creating the array
String[] words = new String[] {"apple", "banana", "cranberry"};
The variable words now holds an array of type String with a length of 3. The elements contained are as specified with the first element bound to index 0, the second element bound to index 1, and so on.
You can think of multi-dimensional arrays as of an array which holds arrays. A 2-dimensional array could look like this:
String[][] twoD = new String[][] {
{"apple", "banana", "cranberry"},
{"car", "ship", "bicycle"}
}
For this twoD[0][2] would be "cranberry" and twoD[1][1] would be "ship". But the number of dimensions of an array does not influence the fact that the keys are integers.
Maps in Java:
Even though Java has no built-in language construct for associative arrays, it offers the interface Map with various implementations, e.g. HashMap. A Map has a type of which the keys are, and a type of which the values are. You can use maps like this:
HashMap<String, String> map = new HashMap<String, String>();
map.put("car", "drive");
map.put("boat", "swim");
System.out.println("You can " + map.get("car") + " a car.");
System.out.println("And a boat can " + map.get("boat") + ".");
This will output:
You can drive a car.
And a boat can swim.
The answer:
The one-to-one way in Java
The answer to your question is that it is not really possible in a reasonable way becasue some of your values are strings, and some are integers. But this would be the most similar code to your PHP array:
//array of HashMaps which have Strings as key and value types
HashMap<String, String>[] cars = new HashMap<String, String>[2];
HashMap<String, String> first = new HashMap<String, String>();
first.put("name", "vauxhall");
first.put("doors", "5");
first.put("color", "black");
HashMap<String, String> second = new HashMap<String, String>();
second.put("name", "peogeot");
second.put("doors", "3");
second.put("color", "red");
//put those two maps into the array of maps
cars[0] = first;
cars[1] = second;
This solution is not very handy, but it is the way that comes closest to your given datastructure.
The cuter way in Java
It seems however, that each of the entries in your PHP array has exactly three properties: name, doors and color. In this case, you may want to create a class Car with these member variables, and store them in an array. This would look like this:
public class Car {
//member variables
public String name;
public int doors;
public String color;
//constructor
public Car(String name, int doors, String color) {
this.name = name;
this.doors = doors;
this.color = color;
}
}
Now, when you have the class Car, you can create an array of all your cars like this:
Car[] cars = new Car[2];
cars[0] = new Car("vauxhall", 5, "black");
cars[1] = new Car("peogeot", 3, "red");
This is the nicer way to do this in Java.
Instead of creating 2D Array you can create 1 class Car
public class Car{
private String carName;
private String color;
private int noOfDoors;
public car(String carName,int door,String color){
this.carName=carName;
this.door=door;
this.color=color;
}
public String getCarName(){
return getCarName;
}
public void setCarName(String carName){
this.carName=carName;
}
// Same getters(getXXX) and setters(setXXX) for other Variables
}
Now create Objects of above class
Car audi=new Car("audi",2,"Black");
Car bmw=new Car("bmw",4,"White");
Now add these to the List<Cars>
List<Car> listOfCars=new ArrayList<Car>();
listOfCars.add(audi);
listOfCars.add(bmw);
Now to Print First Car Name
Car firstCar=listOfCars.get(0);
System.out.println(firstCar.getCarName()); //here getter Method Helped you
I would suggest to get familiar with HashMaps, Maps and ArrayLists. In Java and many other languages is something analogous to a video game cheat.
private static Map<Integer, HashMap<String, String> > carMap = new HashMap<Integer, HashMap<String, String> >();
But in this case you have to understand how would OO principles help you. You can create a class with Car objects and populate a HashMap etc.
class Car {
private String name, colour;....
public Car(){....}
public void setValues(...){....}
}
To achieve better what you want to I would suggest reading this and getting familiar with some design patterns. It's a bit further down the road, but do it for the lulz and seeing what it's out there. Example : http://howtodoinjava.com/2012/10/23/implementing-factory-design-pattern-in-java/
When moving from scripting to strongly typed languages sometimes you have to change your way of thinking too.
Firstly you should create class Car i.e:
public class Car {
enum ColorType {
BLACK, RED;
}
private String name;
private int doors;
private ColorType color;
Car(String name, int doors, ColorType color) {
this.name = name;
this.doors = doors;
this.color = color;
}
public String getName() {
return name;
}
public int getDoors() {
return doors;
}
public ColorType getColor() {
return color;
}
}
And now you can use arrays but better for you will be use ArrayList:
List<Car> cars = new ArrayList<Car>();
cars.add(new Car("vauxhall", 5, BLACK));
cars.add(new Car("peogeot", 3, RED));
for (Car car : cars ) {
System.out.println("Car name is: " + car.getName());
}
It seems what you are trying to achive is an 'array of cars'. So instead of creating an array of arrays, I recommend to literally implement an 'array of cars'.
To do this, I would define the car first, possibly in a different file:
class Car {
//you can make these private and use 'get' and 'set' methods instead
public String name;
public String color;
public int doors;
public Car() {
name = "";
color = "";
doors = 0;
}
public Car(String name, String color, int doors) {
this.name = name;
this.color = color;
this.doors = doors;
}
}
You can use the car structure in an another module like this:
Car[] cars = new Car[100]; //create one hundred cars
cars[11].doors = 4; //make the 12th car's number of doors to 4
You can use more flexible data structures, like Vectors, List, Maps, etc... Search for Java collections, you will find tones of info.
Java is not a loosely typed language, you have to tell the compiler what each variable is going to be. And to store this kind of structured data in Java, you should first declare a class and instantiate objects of that class. Following is how you would achieve the same thing as your PHP code:
class Car {
private String name, color;
private int doors;
Car(String name, int doors, String color) {
this.name = name;
this.doors = doors;
this.color = color;
}
public String getName() {
return this.name;
}
}
public class CarMainClass {
public static void main(String[] args) {
Car[] aCars = new Car[2];
aCars[0] = new Car("vauxhall", 5, "black");
aCars[1] = new Car("peogeot", 3, "red");
System.out.println("First car name is: " + aCars[0].getName());
}
}
Compile using:
javac CarMainClass.java
Then run:
java CarMainClass
You will have to learn the basics of Java first to understand the above code.

How to fill arrays with object parameter based of a value?

If I have a certain number of objects which each take multiple parameters, how can I fill an array with one particular parameter for all objects, but have the order of the elements in the array based off another parameter. For example, I have this code:
public CollegeList(double gpa, int act, int sat, String name, String location){
this.gpa = gpa;
this.act = act;
this.sat = sat;
this.name = name;
this.location = location;
if(act/36.0>sat/2400.0){
this.score = 0.6*gpa*25.0+0.4*(act/36.0)*100.0;
}else{
this.score = 0.6*gpa*25.0+0.4*(sat/2400.0)*100.0;
}
this.scoreDistance = Math.abs(this.score-MainActivity.scoreDouble)/MainActivity.scoreDouble;
}
public double getGpa(){
return this.gpa;
}
public int getAct(){
return this.act;
}
public int getSat(){
return this.sat;
}
public String getName(){
return this.name;
}
public String getLocation(){
return this.location;
}
public double getScore(){
return this.score;
}
public double getScoreDistance(){
return this.scoreDistance;
}
Here, I would like the name parameter for all objects that I may create to populate a String array, but have those names go in ascending order by the double scoreDistance in the array. I'm sorry if the wording of this question is bad, but I hope it makes sense.
1) Create a CollegeList[] or ArrayList<CollegeList> containing the objects you want to sort.
2) Create a Comparator<CollegeList> that compares two CollegeList objects by comparing the scoreDistance. In Java 8 (yes, I know this isn't available for Android, but other readers may find this useful):
Comparator<CollegeList> compareByScoreDistance = (CollegeList a, CollegeList b) -> Double.compare(a.getScoreDistance(), b.getScoreDistance());
In Java 7:
Comparator<CollegeList> compareByScoreDistance = new Comparator<CollegeList>() {
#Override
public int compare(CollegeList a, CollegeList b) {
return Double.compare(a.getScoreDistance(), b.getScoreDistance());
}
};
3) Sort the array or ArrayList using the comparator. If it's an array:
Arrays.sort(theArray, compareByScoreDistance);
If it's an ArrayList, use Collections.sort instead of Arrays.sort.
4) Now you can create the string array by going through the CollegeList[] or ArrayList<CollegeList> and creating an array or ArrayList using getName(). For example, if your list is an ArrayList, then you can use this from #user3717646's answer:
for (CollegeList collegeList : theList) {
nameList.add(collegeList.getName());
}
Or using Java 8:
String[] names = theList.stream().map(CollegeList::getName).toArray(String[]::new);
or
ArrayList<String> names = new ArrayList<>(theList.stream().map(CollegeList::getName).collect(Collectors.toList()));
EDIT: Code has now been tested, and several mistakes fixed.
Try Using ArrayLists. Following sample code is given for two CollegeList objects.
ArrayList<CollegeList> collegeLists=new ArrayList<>(); // To store all CollegeList Objects
ArrayList<String> nameList=new ArrayList<>(); // To store Names
CollegeList cl1=new CollegeList(12, 45, 5, "Name1", "Location1");
CollegeList cl2=new CollegeList(12, 45, 5, "Name2", "Location2");
collegeLists.add(cl1);
collegeLists.add(cl2);
for (CollegeList collegeList : collegeLists) {
nameList.add(collegeList.getName());
}
collegeLists stores all CollegeList objects.
then you can get each and every parameter using get methods and put the in to seperate aarraylists.
If you want to sort the arraylist, You can uose Collections.sort(nameList); to do it.

Categories