I'm starting to learn java and I have a problem with my practice exercise.
This is a snippet of the whole code:
import java.io.PrintWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Connection;
import java.util.ArrayList;
import java.sql.PreparedStatement;
private static final int MAX_LINES = 44;
private static final int REPORT_COL1 = 30;
private static final int REPORT_COL = 15;
private ArrayList<String> errors = new ArrayList<String>();
private ArrayList<String> detailReport = new ArrayList<String>();
private ArrayList<String> summaryReport= new ArrayList<String>();
String firstSortCode= (String) parameters.get("01");
if(firstSortCode==null || firstSortCode.trim().equals("")) {
errors.add("Missing required parameter 01");
invalidParameters= true;
}
else {
for(int i=0; i<SORT_CODES.length; i++) {
if(firstSortCode.equals(SORT_CODES[i][0])) {
pSort1 = SORT_CODES[i][1];
sort1Attr = SORT_CODES[i][2];
sort1GetDescTable = SORT_CODES[i][3];
sort1GetDescCode = SORT_CODES[i][4];
sort1GetDescValue = SORT_CODES[i][5];
val1 = firstSortCode + " - " + pSort1;
break;
}
}
if(pSort1.equals("")) {
errors.add("Invalid value '"+ firstSortCode + "' for parameter 01");
invalidParameters= true;
}
}
String mainSelectSQL ="SELECT shrdgmr_pidm, " +
" shrdgmr_levl_code, "+
" shrdgmr_grst_code, "
" NVL("+sort1Attr+", 'Not Reported') ";
mainSelectSQL =mainSelectSQL +
"FROM shrdgmr " +
"WHERE shrdgmr_pidm is not null " +
//Appends the ORDER BY clause
mainSelectSQL+= "ORDER BY "+sort1Attr;
//Executes the query and obtains the ResultSet
ResultSet rs= sqlStatement.executeQuery();
String sort1Desc= "***";
String Sort1Prev= "*";
//Arrays to hold the student counts for each of the reported sort values
int sort1Count= 0;
int grandTotal = 0;
while(rs.next()) {
String Value1 = rs.getString(2);
if(!Value1.equals(prevSort1Value)) {
String sort1Record= Library.lPad(sort1Count, REPORT_COL, ' ');
if(!sort1Desc.equals("***")) {
if(lineCount[0]+4>MAX_LINES) {
startNewPage(detailReport, pageCount, lineCount, currentDate,
databaseName, pGradTerm, pInitiator, sort1Desc, pSort1, false);
}
detailReport.add(Library.rPad(sort1Desc, REPORT_COL1, ' ')+sort1Record);
summaryReport.add(Library.rPad(sort1Desc, REPORT_COL1, ' ')+sort1Record);
lineCount[0]++;
}
//Resets the counter
sort1Count =0;
sort1Desc= getSortDescription(connection, pSort1, sort1GetDescValue, sort1GetDescTable,
sort1GetDescCode, Value1, errors);
}
sort1Count++;
My question is how to merge duplicate items and sum up their count?
For example the code I have now just prints the following in my summary report,
Architecture 40
Engineering 56
Dentistry 66
Architecture 16
Computer Science 10
Engineering 11
Architecture 5
the output should only be:
Architecture 61
Engineering 67
Dentistry 66
Computer Science 10
Architecture 5
I'm just stuck on how I can do this. I'm thinking of using hashmap or hashset but I'm not sure how. Any help is appreciatd, thank u!
If you really dont want to use the aggregate in sql, you can use HashMap<String, Integer>. Something like that:
Map<String, Integer> aggResult = new HashMap<>();
while (rs.next()) {
String name = rs.getString(NAME_INDEX); //NAME_INDEX - name column index
int value = rs.getInt(VALUE_INDEX); //VALUE_INDEX - value column index
aggResult.merge(name, value, Integer::sum);
//if you dont have java8 use this "if":
/*
if (aggResult.containsKey(name)) {
Integer sum = aggResult.get(name);
aggResult.put(name, sum + value);
} else {
aggResult.put(name, value);
}
*/
}
//test output:
aggResult.forEach((key, value) -> System.out.println(key + ": " + value));
Maybe you will need TreeMap (with or without comparator) instead HashMap for order, null-checks on values from ResultSet or something else.
There had to be aggregate functions involved and theres no charm in having a query which says "aggregate without aggregate"
With this below query does the needful. I have used sum because your o/p doesnt look actually like a count data but sum of already counted records
SELECT NAME, SUM(*)
FROM TABLE GROUP BY
NAME
Related
I've worked on the project in my school and stuck with an error. I cannot run this code since it has an error 'randA2 is already defined in method main(String[])' How can I fix it?
String [] A = {"Russia", "Saudi_Arabia", "Egypt", "Uruguay"};
int A1 = A.length;
int randA1 = (int)(Math.random()*A1);
int randA2 = (int)(Math.random()*A1);
int randA3 = (int)(Math.random()*A1);
int randA4 = (int)(Math.random()*A1);
while(randA1 == randA2) {
int randA2 = (int)(Math.random()*A1);
}
while(randA1 == randA3) {
int randA3 = (int)(Math.random()*A1);
}
while(randA2 == randA3) {
int randA3 = (int)(Math.random()*A1);
}
while(randA1 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
while(randA2 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
while(randA3 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
String AnnounceA1 = A[randA1] +" " + "VS" + " " + A[randA2];
System.out.println(AnnounceA1);
String AnnounceA2 = A[randA3] +" " + "VS" + " " + A[randA4];
System.out.println(AnnounceA2);
I would like to use another way to solve your problem, all you need is :
String[] contries = {"Russia", "Saudi_Arabia", "Egypt", "Uruguay"};
Collections.shuffle(Arrays.asList(contries));
String announceA1 = contries[0] + " VS " + contries[1];
System.out.println(announceA1);
String announceA2 = contries[2] + " VS " + contries[3];
System.out.println(announceA2);
If you want to reassign a new value to a variable in Java, then you don't need to declare its type again. So your first while loop should look like this:
while (randA1 == randA2) {
randA2 = (int)(Math.random()*A1);
}
But besides this, your code has a logical problem, and it won't actually generate 4 unique random numbers. Actually, if you control which numbers you accept, they aren't really random. I would go with this version:
String[] teams = { "Russia", "Saudi_Arabia", "Egypt", "Uruguay" };
Set<Integer> rands = new HashSet<>();
while (rands.size() < teams.length) {
rands.add((int)(Math.random()*teams.length));
}
String AnnounceA1 = teams[rands[0]] +" " + "VS" + " " + teams[rands[1]];
System.out.println(AnnounceA1);
String AnnounceA2 = teams[rands[2]] +" " + "VS" + " " + teams[rands[3]];
System.out.println(AnnounceA2);
The strategy in my suggested version of your code is using a set to hold 4 random integers (which is the number of teams in your example). It is a property of sets that every entry has to be unique. So, if we iterate this set, adding random integers, we will eventually end up with 4 unique random integers. Then, we can use them to choose team names to display in your output message.
You redefine your vars inside the while loops.
Ommit the int declaration iside the while loops:
...
while(randA1 == randA2) {
randA2 = (int)(Math.random()*A1);
}
while(randA1 == randA3) {
randA3 = (int)(Math.random()*A1);
}
...
Java is a type safety language. This is different to for example JavaScript. Inside JavaScipt the JIT (Just in time compiler) would not complain, because it is allowed to redefine variables, event if the type is changing.
I have the following code (with some sample data), and wished to check whether there is any better or performant way to compare each element of the list of map to the subsequent one:
import java.util.*;
public class CompareElements {
private static List<Map<String, String>> sample = new ArrayList<>(0);
private static int MIN = 0;
private static int MAX = 10;
static {
populateListOfMaps();
}
/*
* This is the main part of the question, rest is just to generate test data..
*/
public static void main(String[] args){
// Can we simplify this part using lambda's or any library?
for (int i = 0; i < sample.size() -1; i++) {
for (int j = i+1; j < sample.size(); j++) {
Map<String, String> referenceMap = sample.get(i);
Map<String, String> candideMap = sample.get(j);
if(referenceMap.get("key").equalsIgnoreCase(candideMap.get("key"))){
System.out.println("Equal : " + i + " || " + referenceMap.get("key") + " and "+ j + " || " + candideMap.get("key") + " are pairs");
} else {
System.out.println("Not equal : " + i + " || " + referenceMap.get("key") + " and "+ j + " || " + candideMap.get("key") + " are pairs");
}
}
}
}
private static void populateListOfMaps(){
if(sample.size() <= 10){
Map<String, String> someMap = new HashMap<>(0);
someMap.put("key", "value" + randInt(MIN, MAX));
sample.add(someMap);
populateListOfMaps();
}
}
public static int randInt(int min, int max) {
Random rand = new Random();
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
}
My requirement is to compare each element of the list of maps and then check for equality to remove duplicate, this is a simpler part, but each map in my real time application has 2 keys-values (but both are String.. no custom POJO object).
The above code works but I wish to make this more concise and performant code.
Can we use lambdas or streams?
As you are getting data from MongoDB, I assume you have no control over the schema, so using a POJO isn't a simple option. (it can be done with generated code, but you probably don't want to go there)
What you can do is using groupingBy to change this O(n^2) loops into O(n)
public static void main(String... args) {
List<Map<String, String>> sample = populateListOfMaps();
sample.stream()
.collect(Collectors.groupingBy(m -> m.get("key")))
.forEach((key, list) -> System.out.println(key + " : " + list));
}
private static List<Map<String, String>> populateListOfMaps() {
Random rand = new Random();
return IntStream.range(0, 10)
.mapToObj(i -> {
Map<String, String> someMap = new HashMap<>(2);
someMap.put("key", "value-" + rand.nextInt(10));
return someMap;
})
.collect(Collectors.toList());
}
This will print all the entries which have the same "key" value with O(n) time complexity. e.g.
value-9 : [{key=value-9}]
value-8 : [{key=value-8}, {key=value-8}, {key=value-8}]
value-5 : [{key=value-5}]
value-7 : [{key=value-7}, {key=value-7}]
value-1 : [{key=value-1}]
value-0 : [{key=value-0}]
value-2 : [{key=value-2}]
I'm not realy sure what your exact requirements are so to tackle your question one part at a time:
check whether there is any better or performant way to compare each element of the list of map to the subsequent one:
How about using keySets?
Set<String> s1 = new HashSet< String >(referenceMap.values());
Set<String> s2 = new HashSet< String >(candideMap.values());
// Get intersection of values
s1.retainAll(s2);
// You can also get corresponding keys for each value later
This should reduce your complexity from O(n^2) to O(n)
each map in my real time application has 2 keys-values (but both are String.. no custom POJO object).
Not sure what you mean by real-time. Are the maps changing in real time? Neither your solution nor mine would be thread safe.
Do you mean 2 keys-values for each entry? If you mean 2 values for each key, you would probably override the hashcode(), equals() and your code should work.
Let me know if I misunderstood your question
So I am working on a PostFix calculator that is used in command line for a class project, and I am having a little trouble on developing a memory for it. I have been told to create a hashMap and I have researched it and understand the basics of it. I have the calculating method working, but what I am having trouble trying to implement a way for the user to declare variables. For example this what the user should be able to do:
> a = 3 5 + 1 -
7
> bee = a 3 *
21
> a bee +
28
> bee 3 %
0
> a = 4
4
> 57
57
> 2 c +
c not found
> mem
a: 4
bee: 21
> exit
As you can see the user can declare variables in the format " ="
My problem is, that I am not really sure how to Implement the hashMap, I have tried doing it by setting the variable name for the hashmap by getting it from an array list, and getting the integer value from it by getting the return value from my compute method, but all I get is this error:
>Exception in thread "main" java.lang.NumberFormatException: For input string: "
Error"
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at Program6.main(Program6.java:42)
Here is my code currently:
import java.util.*;
import java.io.*;
public class Program6
{
private static HashMap<String,Integer> memory = new HashMap<>();
public static void main(String args[])
{
System.out.println("Servando Hernandez");
System.out.println("RPN command line calculator");
Scanner scan = new Scanner(System.in);
System.out.print(">");
while(scan.hasNextLine())
{
System.out.print("> ");
String a = scan.nextLine();
String b = "quit";
String c = "mem";
String d = "clear";
if(a.equals(b))
{
System.exit(0);
}
else
{
System.out.println(compute(a));
}
System.out.print(">");
List<String> list = new ArrayList<String>();
if(!a.isEmpty())
{
StringTokenizer var = new StringTokenizer(a);
while(var.hasMoreTokens())
{
list.add(var.nextToken());
}
}
int pos = 0;
if (compute(a) != null)
{
pos = Integer.parseInt(compute(a));
}
memory.put(list.get(list.size()-1),pos);
}
}
public static String compute(String input)
{
List<String> processedList = new ArrayList<String>();
if (!input.isEmpty())
{
String myRegex = "[^a-zA-Z]";
StringTokenizer st = new StringTokenizer(input);
while (st.hasMoreTokens())
{
processedList.add(st.nextToken());
processedList.remove(myRegex);
processedList.remove("=");
}
}
else
{
return "Error";
}
Stack<String> tempList = new Stack<String>();
Iterator<String> iter = processedList.iterator();
while (iter.hasNext())
{
String temp = iter.next();
if (temp.matches("[0-9]*"))
{
tempList.push(temp);
}
else if (temp.matches("[*-/+]"))
{
if (temp.equals("*"))
{
int rs = Integer.parseInt(tempList.pop());
int ls = Integer.parseInt(tempList.pop());
int result = ls * rs;
tempList.push("" + result);
}
else if (temp.equals("-"))
{
int rs = Integer.parseInt(tempList.pop());
int ls = Integer.parseInt(tempList.pop());
int result = ls - rs;
tempList.push("" + result);
}
else if (temp.equals("/"))
{
int rs = Integer.parseInt(tempList.pop());
int ls = Integer.parseInt(tempList.pop());
int result = ls / rs;
tempList.push("" + result);
}
else if (temp.equals("+"))
{
int rs = Integer.parseInt(tempList.pop());
int ls = Integer.parseInt(tempList.pop());
int result = ls + rs;
tempList.push("" + result);
}
}
else
{
return "Error";
}
}
return tempList.pop();
}
}
Does anyone know how i can make the hashMap memory on the post fix calculator work to where the user can assign variables and be able to call them back, or a better way to approach this?
Your problem is you are adding "Error" from you else clause in the compute method and then trying to parse it as a int.
public static String compute(String input)
{
List<String> processedList = new ArrayList<String>();
if (!input.isEmpty())
{
String myRegex = "[^a-zA-Z]";
StringTokenizer st = new StringTokenizer(input);
while (st.hasMoreTokens())
{
processedList.add(st.nextToken());
processedList.remove(myRegex);
processedList.remove("=");
}
}
else
{
return "Error"; //-->> problem
}
Parsing it as an int. This point compute(a) will not be null as it has "Error".
Next step you are trying to parse it as an int.
if (compute(a) != null)
{
pos = Integer.parseInt(compute(a));
}
You can change your code as
if (compute(a) != null && !compute(a).equals("Error"))
{
pos = Integer.parseInt(compute(a));
}
Also you should try you put your Integer.parseInt() code in a try catch.
Your HashMap can be used to store the variable names and their values. The key for the map will be the variable name, and the value is the number assigned to it. You currently have Integer but you might want something that handles decimals if you're going to allow things like a = 10 3 /.
In your compute(..) method you expect the input to be of the form var = <calculation> so you should first parse out the variable name which will be the key used in the hashmap memory, and after computing the calculation, store that result with memory.put(var,result);.
When computing the <calculation> part, if a variable name is encountered, look it up to find its value using memory.get(var) and use that value in the computation. With a postfix calculator, you just have to get the 2 values, followed by the operation and perform the math. The result of that is the first value of the next pair to operate on, and so on until you run out of operations and finally assign the result to the variable.
I am passing a list of Strings to my query(SQL query written) to fetch the required data.
But I am getting this exception:
ora-01795 maximum number of expressions in a list is 1000
I checked that I have more than 1000 entries in the list passed to the query IN parameter.
this is a oracle limitation in the number of list pass in the query.
you will have to chop your query or
provide a subquery/join in the IN clause instead.
You cant have a list with more than 1000 elements in a single "where" condition if you are working with Oracle DB. So you can chop down your "where" condition in multiple "where" conditions and join them with "or" clause.
If you are using hibernate Criteria, you can use below Java method to do this.
Just replace your code where ever you used
criteria.add(Restrictions.in(propertyName, mainList));
with
addCriteriaIn(propertyName, mainList, criteria);
which the method is :
private void addCriteriaIn (String propertyName, List<?> list,Criteria criteria)
{
Disjunction or = Restrictions.disjunction();
if(list.size()>1000)
{
while(list.size()>1000)
{
List<?> subList = list.subList(0, 1000);
or.add(Restrictions.in(propertyName, subList));
list.subList(0, 1000).clear();
}
}
or.add(Restrictions.in(propertyName, list));
criteria.add(or);
}
I solved this by breaking the list into batch of size 1000 and joining it using OR.
e.g.
eid[] array of ids.
If I want to execute this query,
String sql = select * from employee where some conditions and empid in(eid)
I have re-written this query by writing a small piece of code:
String sql = select * from employee where some conditions and (
empid in(empid[0...999]) OR
empid in(empid[1000...1999]) OR
empid in(empid[2000...2999]) OR .... );
Dealing with this error while using hibernate, you have to tackle this issue by chopping the list into batch of 100 and then join the individual results (as shown in the query above).
I don't think that it's a limitation of hibernate for not handling this issue, because it may be the case that this issue is not a case of another DB like MySQL or DB2. Hibernate is a cross-DB ORM framework.
you can create a temporary table, and insert the values you want use in your IN statement, and join the temporary table with your real table. more information about temporary tables.
From dba-oracle.com:
ORA-01795: maximum number of expressions in a list is 1000 tips
Oracle Error Tips by Burleson Consulting (S. Karam)
The Oracle docs note this on the ora-01795 error*: ORA-01795 maximum
number of expressions in a list is 1000 Cause: More than 254 columns
or expressions were specified in a list. Action: Remove some of the
expressions from the list. In the Oracle MOSC Forums, an Oracle user
was attempting to find a way around error code ORA-01795. His
question was answered by Reem Munakash of Oracle:
The limit in Oracle8 is 1000 expressions. There is a bug 495555, filed
against the error text giving the wrong number (254). However, there
may be a further restriction depending on the tool you are using. The
1000 expressions is within sqlplus.
The workaround would be to use a sub-query.
The bug regarding the error message is fixed in 8.1.5.
If you are able to convert your db-side logic from a query into a stored procedure, then you can pass longer arrays (collections) to it.
Here you can find a brief example how to do it. The link to the docs is outdated, so here's a link to the 9i docs http://docs.oracle.com/cd/B10500_01/java.920/a96654/oraarr.htm#1040124
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;
public class ArrayDemo
{
public static void passArray() throws SQLException
{
Connection conn =
new OracleDriver().defaultConnection();
int intArray[] = { 1,2,3,4,5,6 };
ArrayDescriptor descriptor =
ArrayDescriptor.createDescriptor( "NUM_ARRAY", conn );
ARRAY array_to_pass =
new ARRAY( descriptor, conn, intArray );
OraclePreparedStatement ps =
(OraclePreparedStatement)conn.prepareStatement
( "begin give_me_an_array(:x); end;" );
ps.setARRAY( 1, array_to_pass );
ps.execute();
}
}
and the SQL part
create or replace type NUM_ARRAY as table of number;
create or replace
procedure give_me_an_array( p_array in num_array )
as
begin
for i in 1 .. p_array.count
loop
dbms_output.put_line( p_array(i) );
end loop;
end;
Using Java Hibernate, to solve this problem I decided to change the Hibernate-core JAR. I madea a helper class to split an expression in more joins like: ... t.column IN (: list_1) OR t.column IN (: list_2) ... , Then I changed AbstractQueryImpl.expandParameterList method from hibernate to call my method if the collection exceeds the limit.
My hibernate-core version is 3.6.10.Final, but it work fine and for 4.x versions - I tested it.
My code is tested for next cases:
where t.id in (:idList)
where (t.id in (:idList))
where ((t.id) in (:idList))
where 1=1 and t.id in (:idList)
where 1=1 and (t.id in (:idList))
where 1=1 and(t.id) in (:idList)
where 1=1 and((t.id) in (:idList))
where 1=1 and(t.id in (:idList))
where t.id not in (:idList)
where (t.id not in (:idList))
where ((t.id) not in (:idList))
AbstractQueryImpl.expandParameterList :
private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
Collection vals = (Collection) typedList.getValue();
Type type = typedList.getType();
boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptor( name ).isJpaStyle();
String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
String placeholder =
new StringBuffer( paramPrefix.length() + name.length() )
.append( paramPrefix ).append( name )
.toString();
if ( query == null ) {
return query;
}
int loc = query.indexOf( placeholder );
if ( loc < 0 ) {
return query;
}
String beforePlaceholder = query.substring( 0, loc );
String afterPlaceholder = query.substring( loc + placeholder.length() );
// check if placeholder is already immediately enclosed in parentheses
// (ignoring whitespace)
boolean isEnclosedInParens =
StringHelper.getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' &&
StringHelper.getFirstNonWhitespaceCharacter( afterPlaceholder ) == ')';
if ( vals.size() == 1 && isEnclosedInParens ) {
// short-circuit for performance when only 1 value and the
// placeholder is already enclosed in parentheses...
namedParamsCopy.put( name, new TypedValue( type, vals.iterator().next(), session.getEntityMode() ) );
return query;
}
// *** changes by Vasile Bors for HHH-1123 ***
// case vals.size() > 1000
if ((vals.size() >= InExpressionExpander.MAX_ALLOWED_PER_INEXPR) && isEnclosedInParens) {
InExpressionExpander inExpressionExpander = new InExpressionExpander(beforePlaceholder, afterPlaceholder);
if(inExpressionExpander.isValidInOrNotInExpression()){
List<String> list = new ArrayList<String>( vals.size() );
Iterator iter = vals.iterator();
int i = 0;
String alias;
while ( iter.hasNext() ) {
alias = ( isJpaPositionalParam ? 'x' + name : name ) + i++ + '_';
namedParamsCopy.put( alias, new TypedValue( type, iter.next(), session.getEntityMode() ) );
list.add(ParserHelper.HQL_VARIABLE_PREFIX + alias );
}
String expandedExpression = inExpressionExpander.expandExpression(list);
if(expandedExpression != null){
return expandedExpression;
}
}
}
// *** end changes by Vasile Bors for HHH-1123 ***
StringBuffer list = new StringBuffer(16);
Iterator iter = vals.iterator();
int i = 0;
while (iter.hasNext()) {
String alias = (isJpaPositionalParam ? 'x' + name : name) + i++ + '_';
namedParamsCopy.put(alias, new TypedValue(type, iter.next(), session.getEntityMode()));
list.append(ParserHelper.HQL_VARIABLE_PREFIX).append(alias);
if (iter.hasNext()) {
list.append(", ");
}
}
return StringHelper.replace(
beforePlaceholder,
afterPlaceholder,
placeholder.toString(),
list.toString(),
true,
true
);
}
My helper class InExpressionExpander:
package org.hibernate.util;
import org.hibernate.QueryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* Utility class for expand Hql and Sql IN expressions with a parameter with more than IN expression limit size (HHH-1123).
* <br/>
* It work for expression with formats:
* <pre>
*
* where t.id in (:idList)
* where (t.id in (:idList))
* where ((t.id) in (:idList))
* where 1=1 and t.id in (:idList)
* where 1=1 and (t.id in (:idList))
* where 1=1 and(t.id) in (:idList)
* where 1=1 and((t.id) in (:idList))
* where 1=1 and(t.id in (:idList))
*
* where t.id not in (:idList)
* where (t.id not in (:idList))
* where ((t.id) not in (:idList))
* </pre>
* <p/>
* Example:
* <pre>
* select t.id from tableOrEntity t where t.id IN (:idList)
* </pre
*
* #author Vasile Bors
* #since 13/12/2015.
*/
public class InExpressionExpander {
private static final Logger log = LoggerFactory.getLogger(InExpressionExpander.class);
public static final int MAX_ALLOWED_PER_INEXPR = 1000;
private static final int MAX_PARAMS_PER_INEXPR = 500;
private Stack<String> stackExpr = new Stack<String>();
private StringBuilder toWalkQuery;
private final String beforePlaceholder;
private final String afterPlaceholder;
private boolean wasChecked = false;
private boolean isEnclosedInParens = false;
private boolean isInExpr = false;
private boolean isNotInExpr = false;
public InExpressionExpander(String beforePlaceholder, String afterPlaceholder) {
this.toWalkQuery = new StringBuilder(beforePlaceholder);
this.beforePlaceholder = beforePlaceholder;
this.afterPlaceholder = afterPlaceholder;
}
public boolean isValidInOrNotInExpression() {
if (!wasChecked) {
String lastExpr = extractLastExpression();
if ("(".equals(lastExpr)) {
isEnclosedInParens = true;
lastExpr = extractLastExpression();
}
isInExpr = "in".equalsIgnoreCase(lastExpr);
}
wasChecked = true;
return isInExpr;
}
public String expandExpression(List paramList) {
if (isValidInOrNotInExpression()) {
final String lastExpr = extractLastExpression(false);
if ("not".equalsIgnoreCase(lastExpr)) {
isNotInExpr = true;
extractLastExpression(); //extract "not" and consume it
}
extractColumnForInExpression();
StringBuilder exprPrefixBuilder = new StringBuilder();
for (int i = stackExpr.size() - 1; i > -1; i--) {
exprPrefixBuilder.append(stackExpr.get(i)).append(' ');
}
if (!isEnclosedInParens) {
exprPrefixBuilder.append('(');
}
String expandedExpression = expandInExpression(exprPrefixBuilder, paramList);
String beforeExpression = getBeforeExpression();
String afterExpression = getAfterExpression();
String expandedQuery = new StringBuilder(beforeExpression).append(expandedExpression)
.append(afterExpression)
.toString();
if (log.isDebugEnabled()) {
log.debug(
"Query was changed to prevent exception for maximum number of expression in a list. Expanded IN expression query:\n {}",
expandedExpression);
log.debug("Expanded query:\n {}", expandedQuery);
}
return expandedQuery;
}
log.error("Illegal call of InExpressionExpander.expandExpression() without IN expression.");
return null;
}
private String expandInExpression(StringBuilder exprPrefixBuilder, List values) {
String joinExpr = isNotInExpr ? ") and " : ") or ";
StringBuilder expr = new StringBuilder(16);
Iterator iter = values.iterator();
int i = 0;
boolean firstExpr = true;
while (iter.hasNext()) {
if (firstExpr || i % MAX_PARAMS_PER_INEXPR == 0) {
//close previous expression and start new expression
if (!firstExpr) {
expr.append(joinExpr);
} else {
firstExpr = false;
}
expr.append(exprPrefixBuilder);
} else {
expr.append(", ");
}
expr.append(iter.next());
i++;
}
expr.append(')');// close for last in expression
return expr.toString();
}
/**
* Method extract last expression parsed by space from toWalkQuery and remove it from toWalkQuery;<br/>
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
*
* #return last expression from toWalkQuery
*/
private String extractLastExpression() {
return extractLastExpression(true);
}
/**
* Method extract last expression parsed by space from toWalkQuery, remove it from toWalkQuery if is consume = true;<br/>
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space.
*
* #param consum if true the method will extract and remove last expression from toWalkQuery
* #return last expression from toWalkQuery
*/
private String extractLastExpression(final boolean consum) {
int lastIndex = this.toWalkQuery.length() - 1;
String lastExpr;
int exprSeparatorIndex = this.toWalkQuery.lastIndexOf(" ");
if (lastIndex == exprSeparatorIndex) { //remove last space from the end
this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
return extractLastExpression(consum);
} else {
lastExpr = this.toWalkQuery.substring(exprSeparatorIndex + 1, this.toWalkQuery.length());
if (lastExpr.length() > 1) {
if (lastExpr.endsWith(")")) {
//if parens are closed at the end we need to find where it is open
int opensParens = 0;
int closedParens = 0;
int startExprIndex = -1;
char c;
for (int i = lastExpr.length() - 1; i > -1; i--) {
c = lastExpr.charAt(i);
if (c == ')') {
closedParens++;
} else if (c == '(') {
opensParens++;
}
if (closedParens == opensParens) {
startExprIndex = i;
break;
}
}
if (startExprIndex > -1) {
lastExpr = lastExpr.substring(startExprIndex, lastExpr.length());
exprSeparatorIndex = exprSeparatorIndex + startExprIndex
+ 1; // +1 because separator is not space and don't must be deleted
}
} else if (lastExpr.contains("(")) {
int parentsIndex = exprSeparatorIndex + lastExpr.indexOf('(') + 1;
this.toWalkQuery.replace(parentsIndex, parentsIndex + 1, " ( ");
return extractLastExpression(consum);
}
}
if (consum) {
this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length());
}
}
if (consum) {
stackExpr.push(lastExpr);
}
return lastExpr;
}
private String extractColumnForInExpression() {
String column = extractLastExpression();
String beforeColumn = extractLastExpression(false);
long pointIndx = beforeColumn.lastIndexOf('.');
if (pointIndx > -1) {
if (pointIndx == (beforeColumn.length() - 1)) {
throw new QueryException(
"Invalid column format: " + beforeColumn + ' ' + column
+ " . Remove space from column!");
}
}
return column;
}
private String getBeforeExpression() {
return this.toWalkQuery + " (";
}
private String getAfterExpression() {
if (StringHelper.getFirstNonWhitespaceCharacter(afterPlaceholder) == ')') {
return afterPlaceholder;
}
return afterPlaceholder + ") ";
}
}
I am happy to receive any suggestions for improving this solution.
I have a list of dates and prices:
Date Price
1/3/2000 10.00
1/5/2000 10.45
1/7/2000 10.25
... ...
I have a separate list of dates with all dates:
Date
1/1/2000
1/2/2000
1/3/2000
...
I need to have them combined so that the prior price is filled in for the dates that are missing prices:
Date Price
1/1/2000 10.00
1/2/2000 10.00
1/3/2000 10.00
1/4/2000 10.00
1/5/2000 10.45
1/6/2000 10.45
1/7/2000 10.25
... ...
I am currently trying to loop through array lists holding the data but can't line the dates up correctly, especially at the beginning and end. I am using Java/Mysql/JDBC right now but am open to R also. Thanks for any suggestions.
Thanks to everyone for your help. Here's what I ended up doing:
-I created a list of all indexes where the dates matched.
-I then inserted the prices into an array with the same number of elements as the full time list.
-I then created 3 loops, one for the elements before the first matching time, one for the elements after the last matching element and finally one for everything in between.
-These three filled in the prices that were missing.
Just though I'd share. Thanks for all your help.
public static void checkLengths(ArrayList<String> masterTimes, ArrayList<String> testTimes, ArrayList<Double> prices){
ArrayList<Double> temp = new ArrayList<Double>();
ArrayList<Integer> matches = new ArrayList<Integer>();
Double[] temp2 = new Double [masterTimes.size()];
int mt = masterTimes.size();
int tt = testTimes.size();
if(mt == tt){
return;
}else{
int mast = 0;
int test = 0;
String mt1 = masterTimes.get(0);
String tt1 = testTimes.get(0);
test = 0;
for(int i = 0; i < masterTimes.size(); i++){
mt1 = masterTimes.get(i);
tt1 = testTimes.get(test);
System.out.println(" | mt1: " + mt1 + " | tt1: " + tt1);
if(mt1.equals(tt1)){
matches.add(i);
System.out.println("Inserting: " + i);
if(test < testTimes.size()){
test++;
}
if(test == testTimes.size()){
break;
}
}
}
System.out.println("Matches:");
printAL(matches);
// puts in known prices.
for(int i = 0; i < matches.size(); i++){
int g = matches.get(i);
temp2[g] = prices.get(i);
}
System.out.println("FirstPrices:");
printAR(temp2);
// Finds index of first and last matching times.
int matcher1 = matches.get(0);
int ind = matches.size() - 1;
int matcher2 = matches.get(ind);
System.out.println("Matcher1:" + matcher1 + " | Matcher2: " + matcher2);
// If a price is empty/null, it puts the prior price in it.
for(int i = matcher1; i < matcher2; i ++){
System.out.println(i + " | " + temp2[i]);
if(temp2[i] == null){
System.out.println(temp2[i] + " | " + temp2[i-1]);
temp2[i] = temp2[i-1];
}
}
System.out.println("SecondPrices:");
printAR(temp2);
// Deals with start.
for(int i = matcher1; i >= 0; i--){
if(temp2[i] == null){
temp2[i] = temp2[i+1];
}
}
System.out.println("ThirdPrices:");
printAR(temp2);
// Deals with end.
for(int i = matcher2; i < temp2.length; i++){
if(temp2[i] == null){
temp2[i] = temp2[i-1];
}
}
System.out.println("FourthPrices:");
printAR(temp2);
prices.clear();
System.out.println("Final Check:");
for (int i = 0; i < masterTimes.size(); i++){
System.out.println(i + " | " + masterTimes.get(i) + " | " + temp2[i]);
}
}
}
It is difficult to help without looking at the code but it seems like your indexes are not matching up or something is wrong with your looping logic.
Consider using a HashTable or a HashMap using the date strings as keys and price as values.
Loop through your date range one day at a time look up the price in the HashTable, if not found then use the previous price.
This sort of problem does take a bit of doing to do correctly. Sometimes using a flow chart helps if you get stuck.
Try using the following sample code:
import java.sql.*;
import java.util.*;
public class FillDates
{
public static void fillUnknownDates(Connection c) throws SQLException
{
// Loads in a Vector of Strings of all the dates
Statement state = c.createStatement();
ResultSet results = state.executeQuery("SELECT d FROM Dates ORDER BY d;");
Vector<String> dates = new Vector<String>();
while (results.next())
{
dates.add(results.getString("d"));
}
// Load in a list of all date/price combinations
Vector<DatePrice> pairs = new Vector<DatePrice>();
state = c.createStatement();
results = state.executeQuery("SELECT d, p FROM DatePrices ORDER BY d;");
while (results.next())
{
pairs.add(new DatePrice(results.getString("d"), results.getString("p")));
}
// Now go through the two lists and add missing prices
state = c.createStatement();
int dateIndex = 0;
DatePrice last = pairs.get(0), current;
for (int pairIndex = 1; pairIndex < pairs.size(); pairIndex++)
{
current = pairs.get(pairIndex);
while (dateIndex < dates.size() && dates.get(dateIndex).compareTo(current.getDate()) < 0)
{
// Batch things up so it takes less time to run
state.addBatch("INSERT INTO DatePrices VALUES (\""+dates.get(dateIndex)+"\", \""+current.getPrice+"\");");
dateIndex ++;
}
last = current;
}
state.executeBatch();
}
// A convenience class
public static class DatePrice
{
private String date, price;
public DatePrice(String date, String price)
{
this.date = date;
this.price = price;
}
public String getDate()
{
return date;
}
public String getPrice()
{
return price;
}
}
}
Note that it's not complete, and you'll need to change the names of your tables and columns before trying it out.
Okay... I just shooting at it while being on the fon :)
In MySQL, let's assume you got two tables, dates_prices and all_dates. Then LEFT JOIN them on dates and order them by date.
If you use R and MySQL you can use the RMySQL package to load the resulting table to R.
In R you can convert the dates to POSIX with as.POSIXlt. You also might want to use the lagfunction in R (but I am not sure yet if that helps with lags of varying spans).
Apart from that you could use R's ´sqldf` package if you want to try with "plain" R but want to use SQL functionality. If you post some reproducible code to set up the data.. I could try to the give something more concrete back.
EDIT:
The impute package might be what you really looking for... see also here
Here is an R solution.
Uncomment the two install.packages lines if you don't have those packages already installed. Also textConnection(Lines1) and textConnection(Lines2) are just to keep the example self contained and in reality would be replaced with something like "myfile1.dat" and "myfile2.dat" assuming the data is in those files.
It reads in the data creating zoo object z and a Date vector dt. It then merges z with a zero width zoo object (i.e. it has dates but no data) whose date index is made from dt. na.locf (last observation carried forward) fills out the missing values in reverse order since fromLast = TRUE
Lines1 <- "Date Price
1/3/2000 10.00
1/5/2000 10.45
1/7/2000 10.25"
Lines2 <- "Date
1/1/2000
1/2/2000
1/3/2000"
# install.packages("zoo")
# install.packages("chron")
library(zoo)
library(chron)
z <- read.zoo(textConnection(Lines1), header = TRUE, FUN = as.chron)
dt <- as.chron(scan(textConnection(Lines2), skip = 1, what = ""))
na.locf(merge(z, zoo(, dt)), fromLast = TRUE)
The result is:
> na.locf(merge(z, zoo(, dt)), fromLast = TRUE)
01/01/00 01/02/00 01/03/00 01/05/00 01/07/00
10.00 10.00 10.00 10.45 10.25
There are three vignettes (PDF documents) that come with the zoo package and R News 4/1 Help Desk article has info and references on dates.