I have a question about sorting.
int[][] array = {{4,2},{1,7},{4,5},{1,2},{1,1},{4,1}};
After sorting this array, it will become like following.
int[][] array = {{1,1},{1,2},{1,7},{4,1},{4,2},{4,5}};
I fell confused about that, if I perform a sort on rows at first and perform a sort on columns at second, how could I change two of the value in the same time when I sort rows and columns.
see this example...
Sort a two dimensional array based on one column
The first column is a date of format "yyyy.MM.dd HH:mm" and the second column is a String.
Since you say 2-D array, I assume "date of format ..." means a String. Here's code for sorting a 2-D array of String[][]:
import java.util.Arrays;
import java.util.Comparator;
public class Asdf {
public static void main(final String[] args) {
final String[][] data = new String[][] {
new String[] { "2009.07.25 20:24", "Message A" },
new String[] { "2009.07.25 20:17", "Message G" },
new String[] { "2009.07.25 20:25", "Message B" },
new String[] { "2009.07.25 20:30", "Message D" },
new String[] { "2009.07.25 20:01", "Message F" },
new String[] { "2009.07.25 21:08", "Message E" },
new String[] { "2009.07.25 19:54", "Message R" } };
Arrays.sort(data, new Comparator<String[]>() {
#Override
public int compare(final String[] entry1, final String[] entry2) {
final String time1 = entry1[0];
final String time2 = entry2[0];
return time1.compareTo(time2);
}
});
for (final String[] s : data) {
System.out.println(s[0] + " " + s[1]);
}
}
}
Related
I have map Map<String, PriorityQueue> where the queue is ordered based on the score (reverse). I populated the map from a List where key being data.getGroup and value being Dataitself.
Now my usecase is,
if the size of the map is <=3, I just want to return the Data object so I am just doing a poll top values(Data object) for each key and
if the size of the map is > 3 then I need to get 3 values(1 value/key) from the map based on the score.
For eg:
// output should be just Data(17.0, "five", "D"), Data(4.0, "two", "A"), Data(3.0, "three", "B") though there will be only 4 keys (A,B,C,D)
ArrayList<Data> dataList = new ArrayList<Data>();
dataList.add(new Data(1.0, "one", "A"));
dataList.add(new Data(4.0, "two", "A"));
dataList.add(new Data(3.0, "three", "B"));
dataList.add(new Data(2.0, "four", "C"));
dataList.add(new Data(7.0, "five", "D"));
dataList.add(new Data(17.0, "five", "D"));
// output should be just Data(5.0, "six", "A"), Data(3.14, "two", "B"), Data(3.14, "three", "C") as there will be only 3 keys (A,B,C)
ArrayList<Data> dataList2 = new ArrayList<Data>();
dataList2.add(new Data(3.0, "one", "A"));
dataList2.add(new Data(5.0, "six", "A"));
dataList2.add(new Data(3.14, "two", "B"));
dataList2.add(new Data(3.14, "three", "C"));
I tried the below, but is there a better/smarter (optimized) way to do it in Java?
// n = 3
public List<Data> getTopN(final List<Data> dataList, final int n) {
private static final Comparator< Data > comparator = Comparator.comparing(Data::getScore).reversed();
Map<String, PriorityQueue<Data>> map = Maps.newHashMap();
for (Data data : dataList) {
String key = data.getGroup();
if (key != null) {
if (!map.containsKey(key)) {
map.put(key, new PriorityQueue<>(comparator));
}
map.get(key).add(data);
}
}
if (map.size <= n) {
List<Data> result = new ArrayList<Data>();
for (Map.Entry<String, PriorityQueue<Data>> entrySet: map.entrySet()){
PriorityQueue<Data> priorityQueue = entrySet.getValue();
result.add(priorityQueue.peek());
}
return result;
} else if (map.size > n) {
List<Data> result = new ArrayList<Data>();
for (Map.Entry<String, PriorityQueue<Data>> entrySet: map.entrySet()){
PriorityQueue<Data> priorityQueue = entrySet.getValue();
result.add(priorityQueue.peek());
}
return result.stream()
.sorted(Comparator.comparingDouble(Data::getScore).reversed())
.limit(n)
.collect(Collectors.toList());
}
}
Data Object looks like this:
public class Data {
double score;
String name;
String group;
public void setName(String name) {
this.name = name;
}
public void setGroup(String group) {
this.group = group;
}
public void setScore(double score) {
this.score = score;
}
public String getName() {
return name;
}
public String getGroup() {
return group;
}
public double getScore() {
return score;
}
}
Since your starting point is a List<Data>, there’s not much sense in adding the elements to a Map<String, PriorityQueue<Data>> when all you’re interested in is one value, i.e. the maximum value, per key. In that case, you can simply store the maximum value.
Further, it’s worth considering the differences between the map methods keySet(), values(), and entrySet(). Using the latter is only useful when you’re interested in both, key and value, within the loop’s body. Otherwise, use either keySet() or values() to simplify the operation.
Only when trying to get the top n values from the map, using a PriorityQueue may improve the performance:
private static final Comparator<Data> BY_SCORE = Comparator.comparing(Data::getScore);
private static final BinaryOperator<Data> MAX = BinaryOperator.maxBy(BY_SCORE);
public List<Data> getTopN(List<Data> dataList, int n) {
Map<String, Data> map = new HashMap<>();
for(Data data: dataList) {
String key = data.getGroup();
if(key != null) map.merge(key, data, MAX);
}
if(map.size() <= n) {
return new ArrayList<>(map.values());
}
else {
PriorityQueue<Data> top = new PriorityQueue<>(n, BY_SCORE);
for(Data d: map.values()) {
top.add(d);
if(top.size() > n) top.remove();
}
return new ArrayList<>(top);
}
}
Note that the BinaryOperator.maxBy(…) is using the ascending order as basis and also the priority queue now needs the ascending order, as we’re removing the smallest elements such that the top n remain in the queue for the result. Therefore, reversed() has been removed from the Comparator here.
Using a priority queue provides a benefit if n is small, especially in comparison to the map’s size. If n is rather large or expected to be close to the map’s size, it is likely more efficient to use
List<Data> top = new ArrayList<>(map.values());
top.sort(BY_SCORE.reversed());
top.subList(n, top.size()).clear();
return top;
which sorts all of the map’s values in descending order and removes the excess elements. This can be combined with the code handling the map.size() <= n scenario:
public List<Data> getTopN(List<Data> dataList, int n) {
Map<String, Data> map = new HashMap<>();
for(Data data: dataList) {
String key = data.getGroup();
if(key != null) map.merge(key, data, MAX);
}
List<Data> top = new ArrayList<>(map.values());
if(top.size() > n) {
top.sort(BY_SCORE.reversed());
top.subList(n, top.size()).clear();
}
return top;
}
I'm trying find all items in my database that have at least one value in an array that matches any value in an array that I have in my code (the intersection of the two arrays should not be empty).
Basically, I'm trying to achieve this :
public List<Book> ListBooks(string partitionKey, List<string> categories)
{
return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
{
PartitionKey = new PartitionKey(partitionKey)
})
.Where(b => b.Categories.Any(c => categories.Contains(c))
.ToList();
}
With the Book class looking like this :
public class Book
{
public string id {get;set;}
public string Title {get;set;}
public string AuthorName {get;set;}
public List<string> Categories {get;set;}
}
However the SDK throws an exception saying that Method 'Any' is not supported when executing this code.
This doesn't work either :
return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
{
PartitionKey = new PartitionKey(partitionKey)
})
.Where(b => categories.Any(c => b.Categories.Contains(c))
.ToList();
The following code works because there's only one category to find :
public List<Book> ListBooksAsync(string category)
{
return _client.CreateDocumentQuery<Book>(GetCollectionUri())
.Where(b => b.Categories.Contains(category))
.ToList();
}
In plain SQL, I can queue multiple ARRAY_CONTAINS with several OR the query executes correctly.
SELECT * FROM root
WHERE ARRAY_CONTAINS(root["Categories"], 'Humor')
OR ARRAY_CONTAINS(root["Categories"], 'Fantasy')
OR ARRAY_CONTAINS(root["Categories"], 'Legend')
I'm trying to find the best way to achieve this with LINQ, but I'm not even sure it's possible.
In this situation I've used a helper method to combine expressions in a way that evaluates to SQL like in your final example. The helper method 'MakeOrExpression' below lets you pass a number of predicates (in your case the individual checks for b.Categories.Contains(category)) and produces a single expression you can put in the argument to .Where(expression) on your document query.
class Program
{
private class Book
{
public string id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public List<string> Categories { get; set; }
}
static void Main(string[] args)
{
var comparison = new[] { "a", "b", "c" };
var target = new Book[] {
new Book { id = "book1", Categories = new List<string> { "b", "z" } },
new Book { id = "book2", Categories = new List<string> { "s", "t" } },
new Book { id = "book3", Categories = new List<string> { "z", "a" } } };
var results = target.AsQueryable()
.Where(MakeOrExpression(comparison.Select(x => (Expression<Func<Book, bool>>)(y => y.Categories.Contains(x))).ToArray()));
foreach (var result in results)
{
// Should be book1 and book3
Console.WriteLine(result.id);
}
Console.ReadLine();
}
private static Expression<Func<T,bool>> MakeOrExpression<T>(params Expression<Func<T,bool>>[] inputExpressions)
{
var combinedExpression = inputExpressions.Skip(1).Aggregate(
inputExpressions[0].Body,
(agg, x) => Expression.OrElse(agg, x.Body));
var parameterExpression = Expression.Parameter(typeof(T));
var replaceParameterVisitor = new ReplaceParameterVisitor(parameterExpression,
Enumerable.SelectMany(inputExpressions, ((Expression<Func<T, bool>> x) => x.Parameters)));
var mergedExpression = replaceParameterVisitor.Visit(combinedExpression);
var result = Expression.Lambda<Func<T, bool>>(mergedExpression, parameterExpression);
return result;
}
private class ReplaceParameterVisitor : ExpressionVisitor
{
private readonly IEnumerable<ParameterExpression> targetParameterExpressions;
private readonly ParameterExpression parameterExpression;
public ReplaceParameterVisitor(ParameterExpression parameterExpressionParam, IEnumerable<ParameterExpression> targetParameterExpressionsParam)
{
this.parameterExpression = parameterExpressionParam;
this.targetParameterExpressions = targetParameterExpressionsParam;
}
public override Expression Visit(Expression node)
=> targetParameterExpressions.Contains(node) ? this.parameterExpression : base.Visit(node);
}
}
I have the data structre called MyPojo which has fields called time, name and timetaken (all are in Strings). I'm trying to do some grouping as follows:
List<MyPojo> myPojos = Arrays.asList(
new MyPojo("2017", "ABC", "30"),
new MyPojo("2017", "ABC", "20"),
new MyPojo("2016", "ABC", "25"),
new MyPojo("2017", "XYZ", "40")
);
Map<String, Map<String, Double>> resultMap = myPojos.stream()
.collect(Collectors.groupingBy(MyPojo::getName,
Collectors.groupingBy(MyPojo::getTime,
Collectors.averagingDouble(MyPojo::getTimeTakenAsDouble))));
Please note that I've a method called getTimeTakenAsDouble to convert thetimetaken string to double value.
This results as follows:
{ABC={2017=25.0, 2016=25.0}, XYZ={2017=40.0}}
However, my frontend developer wanted the data either in the following format:
{ABC={2017=25.0, 2016=25.0}, XYZ={2017=40.0, 2016=0.0}}
or
[
{
"time": "2017",
"name": "ABC",
"avgTimeTaken": 25.0
},
{
"time": "2017",
"name": "XYZ",
"avgTimeTaken": 40.0
},
{
"time": "2016",
"name": "ABC",
"avgTimeTaken": 25.0
},
{
"time": "2016",
"name": "XYZ",
"avgTimeTaken": 0.0
}
]
I'm thinking to perform iterations on the resultMap and prepare the 2nd format. I'm trying to perform the iteration again on the resultMap. Is there any other way to handle this?
Actually it's pretty interesting what you are trying to achieve. It's like you are trying to do some sort of logical padding. The way I've done it is to use Collectors.collectingAndThen. Once the result is there - I simply pad it with needed data.
Notice that I'm using Sets.difference from guava, but that can easily be put into a static method. Also there are additional operations performed.
So I assume your MyPojo looks like this:
static class MyPojo {
private final String time;
private final String name;
private final String timetaken;
public MyPojo(String time, String name, String timetaken) {
super();
this.name = name;
this.time = time;
this.timetaken = timetaken;
}
public String getName() {
return name;
}
public String getTime() {
return time;
}
public String getTimetaken() {
return timetaken;
}
public static double getTimeTakenAsDouble(MyPojo pojo) {
return Double.parseDouble(pojo.getTimetaken());
}
}
And input data that I've checked against is :
List<MyPojo> myPojos = Arrays.asList(
new MyPojo("2017", "ABC", "30"),
new MyPojo("2017", "ABC", "20"),
new MyPojo("2016", "ABC", "25"),
new MyPojo("2017", "XYZ", "40"),
new MyPojo("2018", "RDF", "80"));
Here is the code that does what you want:
Set<String> distinctYears = myPojos.stream().map(MyPojo::getTime).collect(Collectors.toSet());
Map<String, Map<String, Double>> resultMap = myPojos.stream()
.collect(Collectors.groupingBy(MyPojo::getName,
Collectors.collectingAndThen(
Collectors.groupingBy(MyPojo::getTime,
Collectors.averagingDouble(MyPojo::getTimeTakenAsDouble)),
map -> {
Set<String> localYears = map.keySet();
SetView<String> diff = Sets.difference(distinctYears, localYears);
Map<String, Double> toReturn = new HashMap<>(localYears.size() + diff.size());
toReturn.putAll(map);
diff.stream().forEach(e -> toReturn.put(e, 0.0));
return toReturn;
}
)));
Result of that would be:
{ABC={2016=25.0, 2018=0.0, 2017=25.0},
RDF={2016=0.0, 2018=80.0, 2017=0.0},
XYZ={2016=0.0, 2018=0.0, 2017=40.0}}
I am trying to create a map from a Java stream. I am able to do this easily with javascript and am trying to create the same thing in Java. Here is my data structure:
var slips = [
{
original: 'Y',
lines: [
{
detailLines: {
detailLineId: 111
}
},
{
detailLines: {
detailLineId: 222
}
}
]
},
{
original: 'N',
lines: [
{
detailLines: {
detailLineId: 333
}
},
{
detailLines: {
detailLineId: 444
}
}
]
}
]
Here is how I did it in javascript
var test = slips.reduce((acc, slip) => {
slip.lines.map(line => line.detailLines.detailLineId)
.map(arr => acc[arr] = slip.original);
return acc;
}, {});
to get my result of
{
'111': 'Y',
'222': 'Y',
'333': 'N',
'444': 'N'
}
How do I do this using the Java 8 Stream api? The slips above really is just a POJO. I converted it to a JSON object to do figure it out in js. The real structure of the Objects are
class Slip {
private Boolean original;
private List<Line> lines;
}
class Line {
private List<DetailLine> detailLines;
}
class DetailLine {
private Long detailLine;
}
So what I have started with the Java is
Map<Long, Boolean> results = slips.stream().reduce(new Map<Long, Boolean>, ...)
For me, it looks like a line object contains a single detail line and not a list.
class Slip {
private Boolean original;
private List<Line> lines;
}
class Line {
private DetailLine detailLine;
}
class DetailLine {
private Long detailLineId;
}
Assuming each detail line id is unique, you might use flatMap to create the necessary mappings id -> Boolean, and simply collects them into a map.
import java.util.AbstractMap.SimpleEntry;
import static java.util.stream.Collectors.toMap;
...
Map<Long, Boolean> results =
slips.stream()
.flatMap(s -> s.getLines().stream().map(l -> new SimpleEntry<>(l.getDetailLine().getDetailLineId(), s.getOriginal())))
.collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))
If you indeed have the structure you claimed, you should flatMap twice:
.flatMap(s -> s.getLines().stream().flatMap(l -> l.getDetailLine().stream().map(dl -> new SimpleEntry<>(dl.getDetailLineId(), s.getOriginal()))))
Here is a working example
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<DetailLine> detailedLines1 = new ArrayList<DetailLine>();
detailedLines1.add(new DetailLine(111l));
detailedLines1.add(new DetailLine(222l));
List<DetailLine> detailedLines2 = new ArrayList<DetailLine>();
detailedLines2.add(new DetailLine(333l));
detailedLines2.add(new DetailLine(444l));
Line line1 = new Line(detailedLines1);
Line line2 = new Line(detailedLines2);
List<Line> lines1 = new ArrayList<Line>();
lines1.add(line1);
List<Line> lines2 = new ArrayList<Line>();
lines2.add(line2);
List<Slip> slips = new ArrayList<Slip>();
slips.add(new Slip(true, lines1));
slips.add(new Slip(false, lines2));
Map<Long, Boolean> myResult = new HashMap<>();
slips.stream().map(
slip ->
slip.getLines().stream().map(
line -> line.getDetailLines().stream().map(deadLine -> deadLine.getDetailLine()).collect(Collectors.toList())
).flatMap(Collection::stream)
.map(l -> new AbstractMap.SimpleEntry<>(l, slip.getOriginal()))
).flatMap(l -> l).forEach(System.out::println);
}
}
Output
111=true
222=true
333=false
444=false
I need to create a table with many rows and more than two columns. Suggest a good and fast data structure. I won't be updating and deleting some entries from that table. I will be using just the lookup functions.
For Example, I have a table:
| column 1 | column 2 | column 3|
a | asd | awd | asfc |
b | asgf | aasf | asgfc |
I have:
String a = "column 1"
String b = "b"
String c = find(a,b);
At the end the value in c should be asgf.
You must use associative arrays (so called Dictionaries), based on hash-tables. Average time-complexity for lookup — O(1 + n/k) and O(n) in worst case. You must organize your Table as dictionary of columns (with column names as key). And Column must be dictionary of values (with row names as key)
More info:
http://en.wikipedia.org/wiki/Dictionary_(data_structure)
http://en.wikipedia.org/wiki/Hash_table
Example in C#:
using System;
using System.Collections;
using System.Collections.Generic;
namespace Test {
public class Test
{
class Table {
class Column {
public Dictionary<string, string> cells;
public Column() {
cells = new Dictionary<string, string>();
}
public string Find(string rowName) {
string resultValue;
if (cells.TryGetValue(rowName, out resultValue))
return resultValue;
else
throw new Exception("oops, no such cell");
}
}
Dictionary<string, Column> columns;
List<string> rowNames;
public Table() {
columns = new Dictionary<string, Column>();
rowNames = new List<string>();
}
public void AddColumn(string columnName, params string[] values) {
Column column = new Column();
columns.Add(columnName, column);
// fill new cells
int counter = 0;
foreach (string rowName in rowNames) {
if (counter < values.Length)
column.cells.Add(rowName, values[counter]);
else
column.cells.Add(rowName, "");
counter++;
}
}
public void AddRow(string rowName, params string[] values) {
rowNames.Add(rowName);
// fill new cells
int counter = 0;
foreach (KeyValuePair<string, Column> columnPair in columns) {
Column column = columnPair.Value;
if (counter < values.Length)
column.cells.Add(rowName, values[counter]);
else
column.cells.Add(rowName, "");
counter++;
}
}
public string Find(string columnName, string rowName) {
Column resultColumn;
if (columns.TryGetValue(columnName, out resultColumn))
return resultColumn.Find(rowName);
else
throw new Exception("oops, no such cell");
}
}
public static void Main()
{
Table table = new Table();
table.AddRow("a");
table.AddRow("b");
table.AddColumn("column 1", "asd", "asgf");
table.AddColumn("column 2", "awd", "aasf");
table.AddColumn("column 3", "asfc", "asgfc");
Console.WriteLine(table.Find("column 1", "b") );
}
}
}