I have the following List of String:
{
"Name1,Name2",
"Name2,Name1",
"Name3,Name4",
"Name4,Name3"
}
Without using any Java/C/Python/C++/C# library, I want to remove the duplicates in a way that, it prints:
Name1,Name2
Name3,Name4
One way to remove duplicates would be this:
private static boolean checkIfEquals(String str, String str1) {
HashSet<String> set1 = new HashSet<>(Arrays.asList(str.split(",")));
HashSet<String> set2 = new HashSet<>(Arrays.asList(str1.split(",")));
return set1.equals(set2);
}
Using your same approach, assuming your list of strings is in a variable List<String> strings:
List<String> unique =
strings.stream()
.map(str -> new LinkedHashSet<>(Arrays.asList(str.split(","))))
.distinct()
.map(set -> set.stream().collect(Collectors.joining(",")))
.collect(Collectors.toList());
using c++
int main(){
ios_base::sync_with_stdio(false);
string list[4]={
"Name1,Name2",
"Name2,Name1",
"Name3,Name4",
"Name4,Name3"
};
map<string,bool>exist;
vector<string>flist;
for(int i=0;i<4;i++){
string s=list[i];
sort(s.begin(),s.end());
if(exist[s])continue;
exist[s]=true;
flist.push_back(list[i]);
}
for(auto item :flist)cout<<item<<"\n";
}
Related
Let's say I have an array of strings like this
String[] strings = {"game_2times",
"game_3times",
"game_4times",
"listy_bubenicek",
"listy_peneznieso"};
What I want is to create a HashMap<String, List<String>> to hold the keys and the values. The key and value of each string are separated by a "_". The resultant map for the above strings should look like the following:
"game" -> ["2times", "3times", "4times"]
"listy" -> ["bubenicek", "peneznieso"]
I tried this
for (String item: names) {
for (int i = 0; i < names.length; i++) {
ArrayList<String> c = new ArrayList<>();
if(names[i].contains("game")) {
c.add(item);
}
hashmap.put(item.split("_")[0],c);
}
}
Here is one way using streams.
String[] strings = {
"game_2times","game_3times","game_4times","listy_bubenicek",
"listy_peneznieso"
};
stream the string array using Arrays.stream
Use String.split to split each string on the _ to create an array of s[] of the two elements
use Collectors.groupingBy to group based on a key, in this case, s[0]
then use Collectors.mapping to map to s[1] and put in a list.
Map<String, List<String>> result = Arrays.stream(strings)
.map(str -> str.split("_"))
.collect(Collectors.groupingBy(s -> s[0],
Collectors.mapping(s -> s[1], Collectors.toList())));
result.entrySet().forEach(System.out::println);
prints
game=[2times, 3times, 4times]
listy=[bubenicek, peneznieso]
Here is another option sans streams.
create a map to hold the results
Iterate thru the array, splitting the string as before.
computeIfAbsent will use the supplied argument as a key if it isn't present and will create and return the value of the function, in this case, an ArrayList.
then this and subsequent references to that key will add the value to the list for that key.
Map<String, List<String>> result = new HashMap<>();
for(String str : strings) {
String[] strArray = str.split("_");
result.computeIfAbsent(strArray[0], v->new ArrayList<>()).add(strArray[1]);
}
There are more elegant ways of doing this but I will show the simple to understand way:
public static Map<String, List<String>> flatten(List<String> names) {
final Map<String, List<String>> result = new HashMap<>();
for (String item : names) {
final String[] parts = item.split("_");
if (result.containsKey(parts[0])) {
result.get(parts[0]).add(parts[1]);
} else {
final ArrayList<String> c = new ArrayList<>();
c.add(parts[1]);
result.put(parts[0], c);
}
}
return result;
}
I'm trying to refactor this method to use a lambda expression:
public List<String> getHttpsLinksFromCsvList() {
List<String> data = getDataFromCsv();
List<String> httpLinks = new ArrayList<>();
data.forEach(System.out::println);
for (String value : data) {
String[] arrayString = value.split(COMMA_DELIMITER);
for (String item : arrayString) {
if (item.endsWith(".git")) {
httpLinks.add(item);
}
}
}
//httpLinks.forEach(System.out::println);
return httpLinks;
}
Ideally I want to get remove the two nested for loops and optimise it a bit. Is it possible?
Try this:
List<String> httpLinks = getDataFromCsv().stream()
.map(value -> value.split(COMMA_DELIMITER))
.flatMap(Arrays::stream)
.filter(item -> item.endsWith(".git"))
.collect(Collectors.toList());
I am trying to add quotes to data in a CSV file. Below is the approach i have done it. I am sure there is a simpler way using regex or other methods. Would like to know that.
public List<String> addQuotes2List(List<String> list, String delimiter){
List<String> tempList = new ArrayList<>();
String temp="", value;
Integer i=-1, j=0;
for(String s1: list){
//println("S1 - "+s1+" - "+Arrays.asList(s1.split("\\"+delimiter)) );
i++;
tempList = Arrays.asList(s1.split("\\"+delimiter));
//println(tempList);
temp="";j=0;
for(String s2:tempList){
if(j>0)
temp+=delimiter;
//println("S2 - "+s2);
temp+="\""+s2+"\"";
j++;
}
list.set(i, temp);
}
return list;
}
Input
tempList.clear();
tempList.add("Sushanth.Bobby.Lloyds");
tempList.add("Watch.a.lot.of.movies");
tempList.add("main.hobby.is.programming");
tempList.add("programming.is.dangerous.addiction.of.all");
tempList = a.addQuotes2List(tempList,".");
println("tempList - "+tempList.size());
for(String s:tempList)
println(s);
output
tempList - 4
"Sushanth"."Bobby"."Lloyds"
"Watch"."a"."lot"."of"."movies"
"main"."hobby"."is"."programming"
"programming"."is"."dangerous"."addiction"."of"."all"
Thanks,
Sushanth
if you just handle the string .
you may just replce <.> to <"."> and append <"> on starts and ends .
public List<String> addQuotes2List2(List<String> list, String delimiter) {
List<String> tempList = new ArrayList<String>();
// null check list and delimiter
String rStr = "\""+delimiter+"\"";
String rmsg = "";
for (String s1 : list) {
rmsg = s1.replace(delimiter, rStr);
rmsg = "\""+rmsg+"\"";
tempList.add(rmsg);
}
return tempList;
}
regex may no necessary here. (replace and replaceAll made by regex)
If you're using Java 8, you can use streams to make it more readable.
public List<String> addQuotes2List(List<String> list, String delimiter){
return list.stream()
.map(line -> line.split("\\"+delimiter))
.map(this::addQuotes)
.map(entries -> String.join(delimiter, entries))
.collect(Collectors.toList());
}
private List<String> addQuotes(String[] entries) {
return Arrays.stream(entries)
.map(entry -> String.format("%s", entry))
.collect(Collectors.toList());
}
I have a list which contains list of values like below
[a-xyz,b-yzx,c-aaa,d-rrr,a-qqq,b-hhh]
and i need the above list like below
[xyz,yzx,aaa,rrr,qqq,hhh]
There a several things that should be done here. First, you need to get rid of the enclosing [] so the string could be split. Then, you need to actually split it (by commas). Then, for each string, you need to remove the prefix before the -. Java 8's stream give you a pretty neat way of doing this:
List<String> result =
Arrays.stream(str.substring(1, str.length() - 1).split(","))
.map(s -> s.replaceFirst("\\w-", ""))
.collect(Collectors.toList());
EDIT:
In JDK 7 and below the solution would be similar, but you'd have to resort to using loops instead of streams:
String[] arr = str.substring(1, str.length() - 1).split(",");
List<Stirng> result = new ArrayList<>(arr.length);
for (String s : arr) {
result.add(s.replaceFirst("\\w-", ""));
}
Try this
List<String> list = Arrays.asList("a-xyz","b-yzx","c-aaa","d-rrr","a-qqq","b-hhh");
List<String> result = list.stream()
.map(s -> s.replaceFirst("^.*-", ""))
.collect(Collectors.toList());
System.out.println(result);
result:
[xyz, yzx, aaa, rrr, qqq, hhh]
Or Java7
List<String> list = Arrays.asList("a-xyz","b-yzx","c-aaa","d-rrr","a-qqq","b-hhh");
List<String> result = new ArrayList<>();
for (String s : list)
result.add(s.replaceFirst("^.*-", ""));
System.out.println(result);
call me old-fashioned
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Split {
public static void main(String[] args) {
String[] ss = {"a-xyz","b-yzx","c-aaa","d-rrr","a-qqq","b-hhh"};
List<String> ll = new ArrayList<>();
ll.addAll(Arrays.asList(ss));
List<String> result = new ArrayList<>();
for(String s:ll) {
result.add(s.split("-")[1]);
}
System.out.println(result);
}
}
gives
[xyz, yzx, aaa, rrr, qqq, hhh]
You can use same list to store final value using set method of list.
import java.util.ArrayList;
public class SplitList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a-xyz");
list.add("b-yzx");
list.add("c-aaa");
list.add("d-rrr");
list.add("a-qqq");
list.add("b-hhh");
for(String st : list){
int index = list.indexOf(st); //get index of string
list.set(index,st.replaceFirst("\\w-", "")); //set only with required value
}
for(String st : list){
System.out.println(st);
}
}
}
I have an example here which basically returns list based on simple logic
Given an input list and a list of grouping objects, which has a list field, the method should return a list that contains either all the members of grouping.list if the grouping.name matches any of the strings in the input list OR simply add the input string to the returning list.
After I writing this code, I am thinking it could be made simpler in Java 7 and a better example to use Java 8 Streaming API.
public class CollectorExample {
public static void main(String[] args){
List<String> input = new ArrayList<>();
input.add("foo");
input.add("bar");
input.add("foobar");
input.add("java");
List<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
List<String> list2 = new ArrayList<>();
list2.add("spring");
list2.add("multi-threaded");
Grouping g1 = new Grouping("foobar",list1);
Grouping g2 = new Grouping("java",list2);
List<Grouping> groupingList = new ArrayList<>();
groupingList.add(g1);
groupingList.add(g2);
System.out.println(mapAndMerge(input,groupingList));
}
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList){
Set<String> returnDocs = new HashSet<>();
Iterator<String> it = input.iterator();
while(it.hasNext()){
String doc = it.next();
boolean found = false;
for (Grouping lg : groupingList){
if (lg.getName().equals(doc)){
returnDocs.addAll(lg.getList());
found=true;
}
}
if (!found){
returnDocs.add(doc);
}
}
return new ArrayList<>(returnDocs);
}
}
class Grouping {
List<String> list;
String name;
public Grouping(String name, List<String> list){
this.list=list;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
This outputs [spring, bar, world, foo, hello, multi-threaded] which is correct.
Here is my Java 8 syntax that I tried and did NOT work;
// List<String> mergedDocs =
// input.forEach(doc->
// groupingList.stream().map( g -> g.getName().equals(doc) ? e.getList() : doc ).collect(Collectors.toList()));
// return mergedDocs;
You can make this a lot simpler by not using your Grouping class but using a simple Map<String, List<String>> instead. This map would act as the grouping, holding the list for a given name. This also enables to have a much better performance since looking into the map is constant-time (whereas your solution is in linear time since it traverses the grouping to find a matching one).
If you have to use the List<Grouping>, you can still pre-process it to convert into an intermediate Map:
The mapAndMerge method simply becomes:
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList) {
Map<String, List<String>> map = groupingList.stream().collect(Collectors.toMap(Grouping::getName, Grouping::getList));
return input.stream()
.flatMap(s -> map.getOrDefault(s, Arrays.asList(s)).stream())
.collect(Collectors.toList());
}
Each input is flat mapped to the list contained in the map or a default list containing the current element. Then this is collected to a new list. This code prints:
[foo, bar, hello, world, spring, multi-threaded]
You can re-write the mapAndMerge method following way using java 8. But it is not very concise as you like.
public static List<String> mapAndMerge(List<String> input,
List<Grouping> groupingList) {
Set<String> returnDocs = input
.stream()
.map(t -> groupingList
.stream()
.filter(g -> g.getName().equals(t))
.map(v -> v.getList())
.findAny()
.orElse(Arrays.asList(t)))
.flatMap(t -> t.stream())
.collect(Collectors.toSet());
return new ArrayList<>(returnDocs);
}
I think it would be much simple and clearer if you use Map instead of the Grouping class.
So that's what you'll have in the main() method:
Map<String, List<String>> groupingMap = new HashMap<>();
groupingMap.put("foobar", list1);
groupingMap.put("java", list2);
List<String> mergedDocs = new ArrayList<>();
input.stream()
.map(doc -> groupingMap.getOrDefault(doc, Collections.singletonList(doc)))
.forEach(mergedDocs::addAll);
System.out.println(mergedDocs);