I am trying to make a mapping and get some descriptions. The problem I have is that I don't know how to make mapper function with dynamic parameters.
My code is the following:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class TEST{
public static String getDesc(String domain, String code){
String ret = "";
switch(domain){
case "AAA" :
if(code.equals("0")){
ret = "AAA_Descr_0";
}else if(code.equals("1")){
ret = "AAA_Descr_1";
}
break;
case "BBB" :
if(code.equals("0")){
ret = "BBB_Descr_0";
}else if(code.equals("1")){
ret = "BBB_Descr_1";
}
break;
}
return ret;
}
public static void main(String []args){
List<String> cities = Arrays.asList("AAA","AAA","BBB", "BBB");
List<String> codes = Arrays.asList("0","1","0","1");
List<String> descs = codes.stream()
.map(code -> getDesc( "AAA", code))
.distinct()
.filter(code -> code != null && !code.equals(""))
.collect(Collectors.toList());
descs.stream().forEach(System.out::println);
}
}
How can I replace the line
.map(code -> getDesc( "AAA", code))
with something like
.map(code -> getDesc( cities.get(i), code))
in order to get all the descs.
Expected output
AAA_Descr_0
AAA_Descr_1
BBB_Descr_0
BBB_Descr_1
You could try the following with IntStream. it streams a range from 0 to size exclusive, then gets each item in the list and provides it as arguments to your function. Not exactly as "dynamic" as you expect, but its as good as it gets
public static void main(String []args){
List<String> cities = Arrays.asList("AAA","AAA","BBB", "BBBB");
List<String> codes = Arrays.asList("0","1","0","1");
List<String> descs = IntStream.range(0, cities.size())
.mapToObj(i -> getDesc(cities.get(i), codes.get(i)))
.collect(Collectors.toList());
// List<String> descs = codes.stream()
// .map(code -> getDesc( "AAA", code))
// .distinct()
// .filter(code -> code != null && !code.equals(""))
// .collect(Collectors.toList());
descs.stream().forEach(System.out::println);
}
output:
AAA_Descr_0
AAA_Descr_1
BBB_Descr_0
Related
I got:
a list of lists of TextInfo objects. Each TextInfo object contains a piece of text and a toString override method to return the text. By the Y value of a TextInfo we can conclude what TextInfo's are on the same line (custom problem)
I want:
a list of strings. Each string is the result of concatenation of all elements of one sublist. And I want to make use of streams as much as possible.
So far:
List<String> allLinesByYCoordinate = groupAllTextLineByLineBasedOnY(allTextInfosOnPage);
public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
Map<Object, List<TextInfo>> groupedtextInfosPerLine = allTextInfo.stream().
collect(Collectors.groupingBy(x -> x.getY()));
List<String> allLines = new ArrayList<>();
for (Map.Entry<Object, List<TextInfo>> groupedtextInfos: groupedtextInfosPerLine.entrySet()) {
String temp = "";
for (TextInfo textInfo: groupedtextInfos.getValue()) {
temp += textInfo;
}
allLines.add(temp);
}
return allLines;
}
You might agree that the method groupAllTextLineByLineBasedOnY looks a bit too oldschool. I 'm trying to execute a concatenation on each of the lists of TextInfo's in the big list, resulting in a list of strings where each string used to be a list of TextInfo's
I'm hoping to find a concise stream() solution
Let's refactor a little at a time.
First, you should never build a string in a loop, as it can be very inefficient; use StringBuilder instead. However, we can instead stream and collect into a string. Also notice here that we using Map.values() instead of calling getValue() inside the loop.
public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
Map<Object, List<TextInfo>> groupedtextInfosPerLine = allTextInfo.stream()
.collect(Collectors.groupingBy(x -> x.getY()));
List<String> allLines = new ArrayList<>();
for (List<TextInfo> groupedtextInfos: groupedtextInfosPerLine.values()) {
allLines.add(groupedtextInfos.stream().map(Object::toString).collect(Collectors.joining()));
}
return allLines;
}
In the next refactoring, we can get rid of the intermediate list allLines and instead stream the textInfos and collect them into a List:
public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
Map<Object, List<TextInfo>> groupedtextInfosPerLine = allTextInfo.stream()
.collect(Collectors.groupingBy(x -> x.getY()));
return groupedtextInfosPerLine.values().stream()
.map(textInfos -> textInfos.stream().map(Object::toString).collect(Collectors.joining()))
.toList();
}
Finally, we can get rid of the groupedtextInfosPerLine variable:
public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
return allTextInfo.stream()
.collect(Collectors.groupingBy(TextInfo::getY)).values().stream()
.map(textInfos -> textInfos.stream().map(Object::toString).collect(Collectors.joining()))
.toList();
}
I think your looking for a solution like this
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;
class Scratch {
public static void main(String[] args) {
// Setup some example data
List<TextInfo> textInfos = List.of(
new TextInfo("line 3 a", 3),
new TextInfo("line 1 a", 1),
new TextInfo("line 2 a", 2),
new TextInfo("line 1 b", 1),
new TextInfo("line 3 b", 3),
new TextInfo("line 1 c", 1)
);
// This is the actual answer
Collection<String> allLines = textInfos.stream()
.collect(Collectors.toMap(
TextInfo::getY, // line number as key
TextInfo::toString, // convert TextInfo to String
(a, b) -> a + b, // Merge TextInfo on the same line
TreeMap::new)) // Ensure in order
.values();
// You would return allLines from the method
System.out.println(allLines);
}
static class TextInfo {
String text;
int y;
public TextInfo(String text, int y) {
this.text = text;
this.y = y;
}
public int getY() { return y; }
#Override
public String toString() { return text; }
}
}
If you run the code you print
[line 1 aline 1 bline 1 c, line 2 a, line 3 aline 3 b]
I want to replace conventional if else with lambda. Consider following highlighted code, is there some simple way to have this represented with Lambda ?
public class IfElseLambda {
public static void main(String[] args) {
String value = null;
DataObj data = new DataObj();
List<DataObj> dataObjs = data.getDataObjs();
***if (dataObjs != null) {
value = dataObjs.stream().map(dataObject -> getValue(dataObject)).filter(Objects::nonNull).findFirst().orElse(null);
} else {
value = getValue(data);
}***
}
public static String getValue(DataObj dataObj) {
return "Get value from dataObj";
}
}
class DataObj {
List<DataObj> dataObjs;
public List<DataObj> getDataObjs() {
return dataObjs;
}
public void setDataObjs(List<DataObj> dataObjs) {
this.dataObjs = dataObjs;
}
}
One thing you can do is to change the null list to something which results in the same output:
List<DataObj> dataObjs = Optional.ofNullable(data.getDataObjs()).orElse(Collections.singletonList(data));
dataObjs will now be a list with a single element in the case that data.getDataObjs() is null.
Now you don't need the if/else:
value = dataObjs.stream().map(dataObject -> getValue(dataObject)).filter(Objects::nonNull).findFirst().orElse(null);
I your aim is to isolate the logic of your if-else, and potentially allowing it to be replaced, maybe you could do the following :
Your lambda take as input your data list, and gives you back a String value. Therefore, you can use a java.util.Function interface, like this:
Function<List<DataObj>, String> extractor = dataList
-> dataList == null? Stream.of(DEFAULT_DATA_OBJ) : dataList.stream()
.map(dataObject -> getValue(dataObject))
.filter(Objects::nonNull)
.findFirst()
.orElse(null)
Note, you still have a ternary operator (Do not see how you could do without it, because if your list can be null, you cannot even use Stream.concat to protect from empty-list). However, with that construct, the logic of your ternary operator is replaceable if you make the extractor function replaceable in your code.
Exemple:
public static void main(String... args) {
final List<DataObj> dataList = ...;
final DataObj defaultValue = ...;
Function<List<DataObj>, String> extractor = dataList
-> dataList == null? Stream.of(defaultValue) : dataList.stream()
.map(dataObject -> getValue(dataObject))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
doStuff(dataList, extractor);
// Now, if you want to change your extraction logic, do
doStuff(dataList, whatever -> "Return a constant title");
}
public static void doStuff(final List<DataObj> dataList, final Function<List<DataObj, String> titleExtractor) {
// Do stuff here
}
I have modified the code and trying to get an ArrayList and the String stored in an Arraylist of Objects on a specific condition(say 'str' string equal to 2). I'm not able to convert the Stream to ArrayList. Please help me understand what needs to be done to get the ArrayList from this stream.
I have a class 'SampleClass' like below:
import java.util.ArrayList;
public class SampleClass {
String str;
ArrayList<String> al;
String check;
public SampleClass(String str, ArrayList<String> al, String check) {
super();
this.str = str;
this.al = al;
this.check = check;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public ArrayList<String> getAl() {
return al;
}
public void setAl(ArrayList<String> al) {
this.al = al;
}
public String getCheck() {
return check;
}
public void setCheck(String check) {
this.check = check;
}
}
I have another class 'GetTheArrayListStoredInAnotherArrayList' like below where I'm trying to get the ArrayList stored inside the ArrayList of objects. Please correct me where I'm wrong.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
public class GetTheArrayListStoredInAnotherArrayList{
public static void main(String[] args) {
String test = "qw,rer,try,try,erh5,wertgw45t,45";
ArrayList<String> al = new ArrayList<String>();
al.addAll(new ArrayList<String>(Arrays.asList(test.split(","))));
System.out.println(al);
ArrayList<SampleClass> sca = new ArrayList<SampleClass>();
SampleClass sc1 = new SampleClass("1", al,"ch1");
SampleClass sc2 = new SampleClass("2", al,"cc2");
SampleClass sc3 = new SampleClass("3", al,"fr3");
SampleClass sc4 = new SampleClass("4", al,"fg4");
sca.add(sc1);
sca.add(sc2);
sca.add(sc3);
sca.add(sc4);
ArrayList<String> als1 = null;
ArrayList<String> als = sca.stream().filter( s -> s.getStr().equals("2")).flatMap(sc -> sc.getAl().stream()).collect(Collectors.toCollection(ArrayList::new));
System.out.println(als);
String ch = (String) sca.stream().filter(s -> s.getStr().equals("1")).map(ac -> ac.getCheck());
System.out.println(ch);
}
}
I got the below error when I executed the code :
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Cannot cast from Stream<String> to String
at GetTheArrayListStoredInAnotherArrayList.main(GetTheArrayListStoredInAnotherArrayList.java:24)
Not entirely sure what you are trying to do, but you need to change your code a bit:
List<String> als = sca.stream()
.filter(s -> s.getStr().equals("2"))
.flatMap(sc -> sc.getAl().stream())
.collect(Collectors.toList());
A few things :
flatMap must return a Stream (in your case you are returning a List)
Collectors.toList makes no guarantee of the List in returns, so the assignment is to a List, not an ArrayList.
EDIT
This:
Stream<String> stream = sca.stream().filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck());
Will produce a Stream<String>. You can't simply cast that to a String, you have to collect/reduce that to whatever you want. Like let's say a List:
List<String> list = sca.stream()
.filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck())
.collect(Collectors.toList());
Or a single String for example:
String r = sca.stream()
.filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck())
.collect(Collectors.joining(","));
This is actually basic stuff... you should really study some samples and the documentation.
Change
ArrayList<String> als = sca.stream().filter( s -> s.getStr().equals("2")).flatMap( sc -> sc.getAl());
To
ArrayList<String> als = sca.get(0).getAl();
First you have to use List instead of ArrayList. So with List you code will looks like
List<String> als1 = null;
List<String> als = sca.stream().
filter(s -> s.getStr().equals("2")). //Comparing
map(s -> s.getAl()) // Converting List<SampleClass> to list of all al list inside all SampleClass in format List<List<Straing>>
.flatMap(ArrayList::stream) //Creating a flat list from list of list of List :: List<List<Straing>> --To--> List<String>
.collect(Collectors.toList()); // Collecting as list
I have commented this code with details. But here if there are two SampleCalss objects in the list with str=2 then it will merge the al list of both objects. hope it will help you .
I'm trying to get the ArrayList stored inside the ArrayList of objects.
Well, the basic algorithm is as follows: Filter sca so it only leaves elements where str is "2" -> Get a single element from all the left over elements -> Get the al stored inside of that element.
You have done the first part correctly:
sca.stream().filter( s -> s.getStr().equals("2"))
But now you need to get a single element from the filtered result (filter can result in multiple elements being left over), so you call findFirst:
.findFirst().get()
This get call will throw an exception if there is no element left after the filter. If you don't want it to throw an exception, you can replace it with an orElse call:
.findFirst.orElse(new SampleClass("", null))
If you use orElse, the method chain will evaluate to null if no element with str being "2".
Now you just need to get the array list by calling getAl():
.getAl();
Now we combine all this together:
ArrayList<String> als = sca.stream()
.filter( s -> s.getStr().equals("2"))
.findFirst().orElse(new SampleClass("", null)).getAl();
I have two arraylists
ArrayList<File> filesImage= new ArrayList<File>();
ArrayList<File> filesBox= new ArrayList<File>();
I want to merge into third arraylist like this
ArrayList<File[]> combinedFiles=new ArrayList<File[]>();
How can I do this?
Output should be like:
[[ first object of filesImage, first object of filesBox],[second Object],[]]
Given that the two arrays are of equal length that you wish to combine, i'd personally do something like this.
List<File[]> combinedFiles= new ArrayList<File[]>();
for(int i = 0; i < filesBox.size(); i++){
combinedFiles.add(new File[] {filesImage.get(i), filesBox.get(i)});
}
Apologies if my methods are incorrect, its been a while since i've programmed in java.
First, I'd create a class that holds the file references, e.g. like this:
class FileElement {
File image;
File box;
}
Then I'd create a list of those instead of arrays:
List<FileElement> combinedFiles = ...;
Then I'd iterate over both lists simultaneously:
Iterator<File> imgItr = filesImages.iterator();
Iterator<File> boxItr = filesBox.iterator();
//This assumes it's ok if both lists have different sizes.
//If it isn't you could try && instead, i.e. stop once you'd miss an image or a box
while( imgItr.hasNext() || boxItr.hasNext() ) {
FileElement e = ...;
if( imgItr.hasNext() ) {
e.image = imgItr.next();
}
if( boxItr.hasNext() ) {
e.box= boxItr.next();
}
combinedFiles.add( e );
}
Assuming that the two lists are of equal length, here is a solution using Java8 streams and zip().
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class Demo {
public static void main( String[] args ) {
List<String> filesImage = Arrays.asList("a","b","c");
List<String> filesBox = Arrays.asList("1","2", "3");
List<String[]> result = zip(filesImage.stream(), filesBox.stream(), (a,b) -> new String[] {a,b}).collect( Collectors.toList() );
for ( String[] e : result ) {
System.out.println( Arrays.asList(e) );
}
}
public static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
#Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
#Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
public static <T> Stream<T> iteratorToFiniteStream( Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
}
I borrowed the implementation of zip from Karol Krol here. Zip is the name from the functional world for this pattern of combining two lists in this manner. Also note that while Demo uses String's instead of File, the concept remains exactly the same.
Normally I wouldn't answer a question where OP doesn't show what they've tried, but since I'm seeing a flood of incorrect answers and interpretations...
List<File> filesImage= new ArrayList<File>();
List<File> filesBox= new ArrayList<File>();
List<File[]> combinedFiles=new ArrayList<File[]>();
for (int i = 0; i < filesImage.size(); ++i) {
File[] temp = new File[2];
temp[0] = filesImage.get(i);
temp[1] = filesBox.get(i);
combinedFiles.add(temp);
}
Something like this is known as "zipping" in functional programming, by the way. I'd suggest a solution with Java 8 lambdas, but there doesn't seem to be a zip function in Java SE and the above is quite simple.
Should work similar to this pseudo code
List<File[]> merge = new ArrayList<>();
for(int i=0;i<filesImage.legth&&i<filesBox.length;i++{
merge.add(new File[]{i<filesImage.legth?filesImage[i]:null,i<filesBox.legth?filesBox[i]});
}
You can use function toArray of List to make array.
ArrayList<File> filesImage= new ArrayList<File>();
ArrayList<File> filesBox= new ArrayList<File>();
ArrayList<File[]> combinedFiles=new ArrayList<File[]>();
//add content to 2 lists here
File[] arrayFiles;
//add array image
arrayFiles = new File[filesImage.size()];
arrayFiles = filesImage.toArray(arrayFiles);
combinedFiles.add(arrayFiles);
//add array box
arrayFiles = new File[filesBox.size()];
arrayFiles = filesBox.toArray(arrayFiles);
combinedFiles.add(arrayFiles);
System.out.println(combinedFiles);
I have a list of Strings:
List<String> terms = ["Coding is great", "Search Engines are great", "Google is a nice search engine"]
How do I get the frequency of each word in the list:
E.g.{Coding:1, Search:2, Engines:1, engine:1, ....}
Here is my Code:
Map<String, Integer> wordFreqMap = new HashMap<>();
for (String contextTerm : term.getContexTerms() )
{
String[] wordsArr = contextTerm.split(" ");
for (String word : wordsArr)
{
Integer freq = wordFreqMap.get(word); //this line is getting reset every time I goto a new COntexTerm
freq = (freq == null) ? 1: ++freq;
wordFreqMap.put(word, freq);
}
}
An idiomatic solution with Java 8 streams:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class SplitWordCount
{
public static void main(String[] args)
{
List<String> terms = Arrays.asList(
"Coding is great",
"Search Engines are great",
"Google is a nice search engine");
Map<String, Integer> result = terms.parallelStream().
flatMap(s -> Arrays.asList(s.split(" ")).stream()).
collect(Collectors.toConcurrentMap(
w -> w.toLowerCase(), w -> 1, Integer::sum));
System.out.println(result);
}
}
Note that you may have to think about whether upper/lower case of the strings should play a role. This one onverts the strings to lower case, and uses them as the keys for the final map. The result is then:
{coding=1, a=1, search=2, are=1, engine=1, engines=1,
is=2, google=1, great=2, nice=1}
public static void main(String[] args) {
String msg="Coding is great search Engines are great Google is a nice search engine";
ArrayList<String> list2 = new ArrayList<>();
Map map = new HashMap();
list2.addAll((List)Arrays.asList(msg.split(" ")));
String n[]=msg.split(" ");
int f=0;
for(int i=0;i<n.length;i++){
f=Collections.frequency(list2, n[i]);
map.put(n[i],f);
}
System.out.println("values are "+map);
}
Because the answer with Java 8, while being good, does not show you how to parallel it in Java 7 (and beside default implementation is the same than stream), here is an example:
public static void main(final String[] args) throws InterruptedException {
final ExecutorService service = Executors.newFixedThreadPool(10);
final List<String> terms = Arrays.asList("Coding is great", "Search Engines are great",
"Google is a nice search engine");
final List<Callable<String[]>> callables = new ArrayList<>(terms.size());
for (final String term : terms) {
callables.add(new Callable<String[]>() {
#Override
public String[] call() throws Exception {
System.out.println("splitting word: " + term);
return term.split(" ");
}
});
}
final ConcurrentMap<String, AtomicInteger> counter = new ConcurrentHashMap<>();
final List<Callable<Void>> callables2 = new ArrayList<>(terms.size());
for (final Future<String[]> future : service.invokeAll(callables)) {
callables2.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
System.out.println("counting word");
// invokeAll implies that the future finished it work
for (String word : future.get()) {
String lc = word.toLowerCase();
// here it get tricky. Two thread might add the same word.
AtomicInteger actual = counter.get(lc);
if (null == actual) {
final AtomicInteger nv = new AtomicInteger();
actual = counter.putIfAbsent(lc, nv);
if (null == actual) {
actual = nv; // nv got added.
}
}
actual.incrementAndGet();
}
return null;
}
});
}
service.invokeAll(callables2);
service.shutdown();
System.out.println(counter);
}
Yes, Java 8 simplifies the work !
No, I tested it but don't know if it is better than simple loops nor if it perfectly threadsafe.
(And seeing how you define your list, are not coding in Groovy ? There exists parallelism support in Groovy).