i have two list: list1,list2. i traversal two list and combine them into one list. then i use such statement:
if(list1 != null && list2 != null)
{
int i=0,j=0;
while(i<list1.size() || j<list2.size())
{
if((j>=list2.size()) || (curFormater.parse(list1.get(i).RecordDate).compareTo(curFormater.parse(list2.get(j).RecordDate)) < 0)) {
.....
i++;
}
else if((i>=list1.size()) || (curFormater.parse(list1.get(i).RecordDate).compareTo(curFormater.parse(list2.get(j).RecordDate)) < 0))
{
.....
j++;
}
....
}
in these two if-statements, when j>=list2.size() or i>=list1.size(), the latter condition should not be judged,but the JAVA compiler seems all judge them and throw IndexOutOfBoundsException. How can i let java not excute latter condition judgement?
Thanks in advance!
When you want to merge two lists that have different size, the index for the smallest list will always finish iterate earlier than the largest list, so you have to use the && operator.
if(list1 != null && list2 != null)
{
int i=0,j=0;
while(i<list1.size() && j<list2.size())
{
// your code
i++;
j++;
}
// when one of them arrived to end of list to loop will not be executed so you need to check
// those conditions
while (i > list.size() && j < list.size())
{
// your code
j++;
}
while (i < list.size() && j > list.size())
{
// your code
i++;
}
so for optimization, the Time complexity will always O(m+n) if you assume that there are m elements in list1 and n elements in list2, only Two while loops will be executed
Edit
OR and AND operators are Binary Operators, which means they must check the two
conditions that they are combine, while Not is Unary which alwyas need one condition to check..
If you heard about Truth Tables of those operators, so the compilers are programmed to work based on those truth tables, when he see the || (or operator) he checks the conditions with the truth table of OR, when he see && (and operator) he checks the conditions with the truth table of AND.
And since both OR and AND are binary operators both of the condition will be 'juded' even
one of them is true.
OR Truth Table
(assume that A and B are statemnts (conditions) and R is the result
when you do OR on them
-------------------------
A | B | R
-------------------------
True | True | True
-------------------------
True | False | True
-------------------------
False| True | True
-------------------------
False| False | False
-------------------------
// so in Or table, when both of the conditions are false the result is False
// while when one of them is true the result is True
And Truth Table
(assume that A and B are statemnts (conditions) and R is the result
when you do And on them
-------------------------
A | B | R
-------------------------
True | True | True
-------------------------
True | False | False
-------------------------
False| True | False
-------------------------
False| False | False
-------------------------
// so in And table, when both of the conditions are true the result is True
// while when one of them is false the result is False
I hope now you understand why the two conditions must be checked even if one of
them is true..
For example, lets say you have two conditions A and B and you do OR on them and assume A is false
if (A || B)
// some code here
in that case it depends in condition B, if B is false the code will not be executed,
if B is true the code will be executed..
I know you asked why if A is true it is still checking the B condition, so it's because
that OR is a binary operator..
Related
I have a database, with names and true or false values.
I have for example 10 rows and four of them have got the value "true".
name | value
-------------
1 | false
2 | true
3 | false
4 | true
5 | false
6 | false
7 | true
8 | false
9 | false
10 | true
my try is:
val c = db!!.rawQuery("SELECT COUNT(*) FROM table GROUP BY value HAVING value = \"true\"", arrayOf())
c.moveToNext()
Log.e("OUTPUT", c.toString())
but the log I will get is:
E/OUTPUT: android.database.sqlite.SQLiteCursor#b0b20ea
So my question is, how to get the countnumber as a usable Integer value?
First correct your query like this:
val c = db!!.rawQuery("SELECT COUNT(*) FROM table WHERE value = 'true'"
because you don't want to group by value but count the rows of the tables that contain the value 'true'.
I assume that the column value contains strings.
If it contains booleans then the query should be:
val c = db!!.rawQuery("SELECT COUNT(*) FROM table WHERE value"
Then you can extract the value of the 1st and only column of the cursor by c.getInt(0):
Log.e("OUTPUT", c.getInt(0).toString())
I am using Firebase Realtime databse in an android app ,
the app contains articles and I want to add the Number of views to the articles through all the app users .
The database contains a child (ViewsCount) its value (is a number)
I need that value to be updated (incremented by 1) every time an article is viewed by a new user (Concept similar to YouTube video views)
The user have to be authenticated anonymously to update the value, but should not be able to do it more than once.
I got the logic integrated into the app to do just that and works great.
But what I am afraid of is if someone tries to connect to the database outside of my app (using their own code) and keeps incrementing the value.
My current security rules are:
"ViewsCount"
{
".write": "newData.exists() && auth!==null",
".validate" "newData.val()===data.val()+1"
}
"users": {
"$user_id": {
".write": "auth.uid===$user_id",
".read": "auth.uid===$user_id ",
}
}
is there a way to restrict the update of a that value to only once per user via security rules ?.
Error :
Update :
screenshot from Simulator
Database structure
There sure is, but you'll have to do some extra work. The first thing is to track in the database who has already viewed each article. For example keep a list of their UIDs, like this:
viewedBy: {
uidOfKarim: true,
uidOfPuf: true
}
Now when somebody views an article, they write their UID to viewedBy, and at the same time increment the view count for that article. In code that can be as simple as:
DatabaseReference countRef = FirebaseDatabase.getInstance().getReference("ViewsCount");
countRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String uid = FirebaseAuthentication.getCurrentUser().getUid();
Long newCount = dataSnapshot.getValue(Long.class) + 1;
Map<String, Object> updates = new HashMap<String, Object>();
updates.put("viewedBy/"+uid, true);
updates.put("ViewsCount", newCount);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
So this code send the UID of the user and the new view count to the database. Now we update the security to only accept the write operation if the UID hasn't been stored yet and if the count is incremented by 1:
"viewedBy": {
"$user_id": {
".validate": "
auth.uid===$user_id && !data.exists() && newData.exists() &&
newData.parent().parent().child('ViewsCount').val() === data.parent().parent().child('ViewsCount').val()+1
"
}
},
The syntax is a bit long, since it checks both the ViewsCount and the ViewedBy data. The write only succeeds if it increments by 1 and is from a user who hasn't been counted yet. If they have been counted before, or don't increment by 1, the write is rejected.
There is one edge case here: if multiple users view at almost the same time, one of their writes may be rejected, because they didn't actually increment the value:
user1 user2 database
| | |
read_count -------------------> |
| | |
| read_count --------> |
| | |
| <------------------- 2 |
| | |
| | <------------ 2 |
| | |
set_count 3 -------------------> |
| | |
| set_count --------> |
| | |
| <------------------- ok |
| | |
| | <------------ ACCESS |
| | DENIED |
| | |
In this case you should retry the update operation, reading the new value and determining the correct count based on that. This is actually how Firebase transactions work behind the scenes. Alternatively, you can first read from viewedBy to see if the current user has already been counted, before trying to count them. But either way: with these rules, each user can only count once.
Update: this is how I tested these rules in the simulator:
And this is the JSON I tested with:
"52469568": {
"ViewsCount": 10,
}
This is the security rules that worked in case any one else have the same issue:
{"rules": {
"views": {
".write": "auth != null",
".validate": "newData.hasChildren(['ViewsCount', 'viewedBy'])",
"ViewsCount": {
".read": "auth != null",
".validate":"data.parent().child('viewedBy').child(auth.uid).exists()== false"
},
"viewedBy": {
"$user_id": {
".validate": " auth.uid===$user_id && !data.exists() && newData.exists() &&
newData.parent().parent().child('ViewsCount').val() ===
data.parent().parent().child('ViewsCount').val()+1
&& newData.val()===true"}},
},
In Kotlin refenrence, it is written as multiple conditions can be matched using comma (,) inside a when control flow. For example-
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
Here, in the first condition comma works like an OR operator.
Is there any way to write an expression to match AND condition inside when?
Yes. Although the syntax is somewhat different:
when {
x % 5 == 0 && x % 3 == 0 -> println("foobar")
x % 5 == 0 -> println("bar")
x % 3 == 0 -> println("foo")
}
When I do this selcect:
SELECT tabuoj.id, tabuoj.tabuo, montritaj_tabuoj.id, montritaj_tabuoj.id_de_tabuo
FROM tabuoj LEFT JOIN montritaj_tabuoj
ON (tabuoj.id=montritaj_tabuoj.id_de_tabuo)
I get:
id | tabuo | id | id_de_tabuo |
1 | dom | 2 | 1 |
2 | samochód | null | null |
3 | okno | 1 | 3 |
but if I add where in which I compare id_de_tabuo column to null I dont't get any result.
SELECT tabuoj.id, tabuoj.tabuo, montritaj_tabuoj.id, montritaj_tabuoj.id_de_tabuo
FROM tabuoj LEFT JOIN montritaj_tabuoj
ON (tabuoj.id=montritaj_tabuoj.id_de_tabuo)
WHERE montritaj_tabuoj.id_de_tabuo=null
If I compare for example 1 like this:
SELECT tabuoj.id, tabuoj.tabuo, montritaj_tabuoj.id, montritaj_tabuoj.id_de_tabuo
FROM tabuoj LEFT JOIN montritaj_tabuoj
ON (tabuoj.id=montritaj_tabuoj.id_de_tabuo)
WHERE montritaj_tabuoj.id_de_tabuo=1
I get correct:
id | tabuo | id | id_de_tabuo |
1 | dom | 2 | 1 |
But I need rows with null in id_de_tabuo column. What I do wrong?
Try with IS NULL instead of = null like this
SELECT tabuoj.id, tabuoj.tabuo, montritaj_tabuoj.id, montritaj_tabuoj.id_de_tabuo
FROM tabuoj LEFT JOIN montritaj_tabuoj
ON (tabuoj.id=montritaj_tabuoj.id_de_tabuo)
WHERE montritaj_tabuoj.id_de_tabuo is null
Reason: Actually
montritaj_tabuoj.id_de_tabuo is null checks that the value is null. whereas
montritaj_tabuoj.id_de_tabuo = null checks that the value is equal to NULL which is never true.
Even NULL is not equal to NULL. You can check using this.
if(null = null)
print 'equal'
else
print 'not equal'
it will print not equal. Now try this
if(null is null)
print 'equal'
else
print 'not equal'
it will print equal.
Try just two simple select like
select * where id = null
and
select * where id != null
It's a common misconception about null.
Think null as the english word "whatever" in a opposition to "nothing" and you ill start to understand it better.
As sachim answered use
IS NULL
on your predicates.
When you filter on a left joined table in the where clause, the join becomes an inner join. To solve the problem, filter in the from clause, like this:
FROM tabuoj LEFT JOIN montritaj_tabuoj
ON (tabuoj.id=montritaj_tabuoj.id_de_tabuo)
and montritaj_tabuoj.id_de_tabuo is null
where clause starts here
I have 3 tables, USER, ENTRY (for entered products, not necessary to create a PRODUCT table), and USER_COLLECTION, which is a table inbetween USER and ENTRY, because an entry can have multiple users.
Basically:
User = USERID | USER_NAME
Entry = ENTRYID | ENTRY_NAME | ENTRYPRICE | ENTRY_DATE
Collection = COLLECTIONID | ENTRYID | USERID
I have a table with users that persist throughout the project. They can create entries (which is usually some kind of product with a price) and they can link multiple users to a certain entry (which can be selected from a list, hence the users persist throughout the project).
So for instance, my tables look like this:
User
--------------------------
user_id | user_name
--------------------------
1 | 'FOO'
2 | 'BAR'
3 | 'FOOBAR'
ENTRY
-----------------------------------------------------------------------
entryid | entry_name | entry_price | entry_date
-----------------------------------------------------------------------
0 | 'Banana' | 2.50 | 12/12/2012
COLLECTION
---------------------------------------
collectionid | entryid | userid
----------------------------------------
0 | 1 | 1
1 | 1 | 2
2 | 1 | 3
I have a Banana, with a price of 2.50 and 3 users linked to it, Foo, Bar and Foobar.
Now, I want to use this in my app and get the data; except I don't know where to start. I tried selecting the entry data, using that id to loop through the collection data, but that would mean I have two cursors open and it wouldn't work. Tried creating a join but I couldn't really make a good one, mainly because:
JOIN
---------------------------------------
collectionid | entryname | username
----------------------------------------
0 | Banana | FOO
1 | Banana | BAR
2 | Banana | FOOBAR
I can't iterate through this, because I would create multiple of the same entry objects in my Android code...
Hope I'm being clear on this.
if (cursor2.moveToFirst()) {
do {
Item i = new Item(<GET STUFF FROM CURSOR>);
i.addUser(new Person(<GET STUFF FROM CURSOR>)));
Log.d("TAG", i.getUsersPaying().size() + "");
} while (cursor2.moveToNext());
}
If I use this, I create mulitple instances of Item i. They'll all be Banana, whilst I should only have 1 item Banana, with multiple users added to it.
First, you might want to consider returning the IDs from your tables in your join query. Things would be a little easier if you returned the entryid column.
Just make a Map<Integer, Item> to store items that you have seen already in your loop. As you examine each cursor, check the map to see if you already have an instance. If you don't, just make a new one and insert it.
Let's assume your query results are:
JOIN
----------------------------------------------------
collectionid | entryname | entryname | username
----------------------------------------------------
0 | 1 | Banana | FOO
1 | 1 | Banana | BAR
2 | 1 | Banana | FOOBAR
2 | 2 | Apple | FOOBAR
You can modify your code as follows:
Map<Integer, Item> items = new HashMap<Integer, Item>();
if (cursor2.moveToFirst()) {
do {
int itemId = cursor2.getInt(1);
Item i;
if (items.containsKey(itemId))
i = items.get(itemId);
else
{
i = new Item(<GET STUFF FROM CURSOR>);
items.put(itemId, i);
}
i.addUser(new Person(<GET STUFF FROM CURSOR>)));
Log.d("TAG", i.getUsersPaying().size() + "");
} while (cursor2.moveToNext());
}
You need to maintain a dictionnary of your entities which are already loaded in memory. For instance in a background fragment which would be retained.
Basically you would do:
Item i = cacheFragment.createOrGetEntry( cursor.getLong( ENTRY_ID_COLUMN_INDEX ) );
Person p = cacheFragment.createOrGetPerson( cursor.getLong( PERSON_ID_COLUMN_INDEX ) );
Of course, your query must also return the IDs of all the rows you need (entryId and personId). But a join query is the way to do it efficiently, so keep what you did about that and just add the two missing ID columns.
a createOrGetPerson method would look like:
public Person createOrGetPerson(long id) {
Entry<Long, Person> p = personDictionnary.get( id ); // can be a HashMap or even better, a SparseArray
if (p==null) {
p = new Person(id);
personDictionnary.put(p); // Remember it for next time
}
return p;
}
You should also have a look at data persistence frameworks or ORM frameworks which are made to deal with this kind of problem (e.g. Hibernate, even though I don't know if that is working with Android).