How to output the firebase snapshot in reverse order? - javascript

I am trying to get the latest 3 data from the database and display them in reverse order in the HTML page.
Code:
var refForevent = database.ref('/events/post');
refForevent.orderByChild("intro").limitToLast(3).on('child_added', function(snapshot){
var eventlist = []
eventlist.push(snapshot)
console.log(eventlist)
eventlist.reverse()
document.getElementById("event1date").innerHTML = moment(eventlist[0].intro).format("MMM Do YYYY");
document.getElementById("event1title").innerHTML = eventlist[0].title;
document.getElementById("event2date").innerHTML = moment(eventlist[1].intro).format("MMM Do YYYY");
document.getElementById("event2title").innerHTML = eventlist[1].title;
document.getElementById("event3date").innerHTML = moment(eventlist[1].intro).format("MMM Do YYYY");
document.getElementById("event3title").innerHTML = eventlist[1].title;
})
Output: Output that I am getting
Database:

I see that the field intro contains a date.
Here is one solution:
Take the value of this field
Remove the hyphen separators (e.g. from 2020-12-10 you get the number 20201210)
Multiply this number by -1
Store the resulting value in another field
Sort on this field
Alternatively (and more elegant...), use the date to create a Timestamp, multiply it by -1 and use it as explained above, i.e. store the resulting value in another field and sort on this field.

Since you're listening to the child_added event, your function gets called with each post node that matches the query in the order in which Firebase returns them. So your eventlist will only ever contain one node at a time.
To reverse the items, you can either get Firebase to return the values in reverse order, as Renaud suggest (I'll also link some answers below), or you can listen to all results in once go and then reversing client-side (as your code already seem to be trying). The latter would look something like:
var refForevent = database.ref('/events/post');
refForevent.orderByChild("date").limitToLast(3).on('value', function(results){
var eventlist = []
results.forEach(function(snapshot) {
eventlist.push(snapshot.val())
});
eventlist.reverse()
console.log(eventlist);
...
})
So this:
Listens to the value event, instead of child_added, so that it gets all matching child nodes in one go.
If then loops over the results, adding them to the array,
It calls .val() on each child snapshot, to get the value from it.
For more on descending sorting, also see:
Display posts in descending posted order (the oldest I could find describing the trick with negative values)
firebase -> date order reverse (with many more links from there)
Sorting in descending order in Firebase database

Related

Double sort list in react-native

I'd like to know how to "double sort" items in react native in order to obtain a double criteria sorted list.
The data I receive from BE is a list of match: every match is an object containing 2 players, a time and a competition.
I d'like to sort items by time (so that a today match is showed before a tomorrow match) and by competition (so that a more important competition is showed before a lower one).
If two or more matches are played on the same day and within the same competition, the earlier match is showed before.
A schema better shows what I mean: I'd like to customize the UI oc the day-row, the competition-row and the match-row.
The data I receive from BE are formatted in this way:
How could I obtain what I want? I've tried with sortable list but with no result.
TY in advance
The javascript docs for sort show that you can sort using an arbitrary function. I would write a function which first compares days and then compares competitions:
function compareByDayThenByCompetition(a, b) {
if (a.day < b.day) {
return -1;
}
if (a.day > b.day) {
return 1;
}
return a.competition - b.competition;
}
events = [{day:3, competition:2}, {day:3, competition:3}, {day:3, competition:1}, {day:2, competition:1}]
console.log(events.sort(compareByDayThenByCompetition))
The best way to filter such data is.
Filter the complete list and make an array of object the object should be grouped by same timestamp.
2)Now iterate the filtered object array and filter the object value.

How to use a index value of a dynamic array (which get filled by a click event) for filtering a new array

I'm working on a filter, which filters a array of nested arrays down to the value of one last index.
This happens in 5 steps. At each step you choose which index value (string) get used to filter the array further.
Example: You have 5 categories, each have 6 themes. Each of these 6 themes has 6 focusses(sub themes). Each focus has 6 questions. Each question has 1 answer. First you pick a categorie. This narrows the arrays down to all arrays with that categorie. Then a theme, which narrows the array down to only the arrays with that theme... etc...
So far I managed to filter down to the right question.
You can find the code here: https://github.com/okestens/vertical-filter.git
To get this work, I hardcoded the string "Deskundigheid" as a condition for the equality operator (===) that get used for the filter.
Example:
// FILTER QUESTIONS // I tried to use state.focus[0] but it does not work
let unique_questionsA = [. // now this is hardcoded
...new Set(formsA.filter((i) => i[2] === "Deskundigheid").map((i) => i[3])),
]; --------------
// FUNCTION
function displayQuestionsA() {
state.questions = [];
unique_questionsA.forEach(function (question, index) {
document.getElementById("question" + index).innerHTML = question;
state.questions.push(question);
});
------
// the state object
let state = {
category: [],
themes: [],
focus: [],
question: [],
answer: [],
};
But. What I want this filter to use is not a hardcoded string (deskundigheid) but the exact string that is visible in the div (coming from a click event which creates this filtered array and get stored in the state object). See image.
I thought: I need to track these arrays (with an object called 'state', capturing these dynamic arrays). If I then want to filter the right questions, by using the value (string) of the chosen focus (For example 'Deskundigheid', which is visible to the user), I just refer to the corresponding index value (state.focus[0]) of that chosen focus string, coming from the dynamic state object.
But, if I use the index state.focus[0] for the filter which creates the questions array, I get an empty array :(
My thought: Although the empty focus array (inside the state object), which get filled by a click event, eventually is filled with the right strings, the filter for the new array (unique_questionsA), which uses 'state.focus[0]' does not read the filled array as ‘filled’ but as empty.
I have not idea why :(
I hope I'm clear. If so, and you maybe have a clue, I would love to have a chat! Thanks O
The question can be summed up to
how do I get the text of the element when clicked, in an onclick event
listener callback function.
Your focusScript.js can be modified to
function displayQuestionsA(e) {
state.questions = [];
let unique_questionsA = [...new Set(formsA.filter((i) => i[2] === e.target.innerText).map((i) => i[3]))];
}
document.querySelector(".focus__A").addEventListener("click", displayQuestionsA);
Notice the e.target.innerText which contains the text inside the element that triggered the event(which you clicked).
if I got you correctly - both map and filter functions can give your callback second parameter - the index.
arr.map((n,i)=>{console.log(`[${i}]:${n}`)})

WebdriverIO loop through element list

So I have a table of 250 of rows, and I want to just get all the values from one column and check if they meet the required criteria:
const rows = browser.elements(selector..);
const numbers = [];
rows.value.forEach(cellData => {
const value = browser.elementIdText(cellData.value.ELEMENT).value;
// some logic to check if the value is ok
numbers.push(value);
});
// check if all numbers are sorted correctly
, but it most of the time it fails on the line (it says stale element reference: element is not attached to the page document):
const value = browser.elementIdText(cellData.value.ELEMENT).value;
I tried doing cellDate.getText(), but there was a Java socket error, could someone help? I assume the selector is not attached to the page as indicated, but I can't figure my head out how to just loop through them all.
I had a solution similar to your method before and while it seems to work, I think there might just be some slight adjustments to your code to get what you want. I never had much luck chaining from the end of the elementIdText call.
Step 1: Grab all the Data (browser.elements or browser.$$):
let cellData = browser.$$('selector that matches desired Column Data')
The above returns an array of JSON WebElements. And as you know you can correctly loop through the array looking at the "values". If you use the selector that matches the Column Values you're looking for you should have all similar data stored in the element.value.ELEMENT.
Step 2: Loop through the cellData array and pluck out the text values of the ELEMENT using browser.elementIdText()
cellData.forEach((elem) => {
let number = browser.elementIdText(elem.value.ELEMENT)
//elementIdText also returns a JSON WebElement so it's number.value
if(number.value === <condition>) {
console.log('number looks good')
//perform other on value logic
}
})
//perform other logic still in loop EX: array.push()
})
I hope this helps! Let me know if you hit any snags!

Display different text everytime you call onclick function

I have this code where I am trying to pull users from my firebase database:
function pullFromDB(){
usersRef.on('value', function(snapshot) {
function next_user(){
for (user in snapshot.val().users){
document.getElementById("users").innerHTML = user
}
}
So far it will return just one of the users as I believe it must be going through the whole users array and then putting the last name into the "users" element.
How can I iterate to the next item in the array and display a different name every time I click a button?
I have no idea what's return from DB but first try the console.log() to see what's the type of result. This will give you the answer if you are working on array or other data type.
Then if it's an array, your code:
for (user in snapshot.val().users){
document.getElementById("users").innerHTML = user
}
It iterates the whole array and .innerHTML puts user string as the last iteration. If you want to change it each time you click a button you need a variable that will contain inteager of index that's currently aplied to id="users".
Then for each click you need to increase the variable by one.
Here is an example of working with arrays in JS. http://www.w3schools.com/js/tryit.asp?filename=tryjs_array_element
Your are iterating through the whole array and only the last one is possible shown. You just need to assign an index and use it.
I don't know your remaining code, but by the given information, this should to the trick:
i = 0;
function pullFromDB(){
usersRef.on('value', function(snapshot) {
lastnames = Object.keys(snapshot.val().users); // get all keys, in your case the last names
function next_user(){
user = lastnames[i];
document.getElementById("users").innerHTML = user;
i++; // increase after click
// when you want to access more information of the user, you can do it like this (as an example)
// user_id = snapshot.val().users[user].id;
}

How to change result position based off parameter in a mongodb / mongoose query?

So I am using mongoose and node.js to access a mongodb database. I want to bump up each result based on a number (they are ordered by date created if none are bumped up). For example:
{ name: 'A',
bump: 0 },
{ name: 'B',
bump: 0 },
{ name: 'C',
bump: 2 },
{ name: 'D',
bump: 1 }
would be retreived in the order: C, A, D, B. How can this be accomplished (without iterating through every entry in the database)?
Try something like this. Store a counter tracking the total # of threads, let's call it thread_count, initially set to 0, so have a document somewhere that looks like {thread_count:0}.
Every time a new thread is created, first call findAndModify() using {$inc : {thread_count:1}} as the modifier - i.e., increment the counter by 1 and return its new value.
Then when you insert the new thread, use the new value for the counter as the value for a field in its document, let's call it post_order.
So each document you insert has a value 1 greater each time. For example, the first 3 documents you insert would look like this:
{name:'foo', post_order:1, created_at:... } // value of thread_count is at 1
{name:'bar', post_order:2, created_at:... } // value of thread_count is at 2
{name:'baz', post_order:3, created_at:... } // value of thread_count is at 3
etc.
So effectively, you can query and order by post_order as ASCENDING, and it will return them in the order of oldest to newest (or DESCENDING for newest to oldest).
Then to "bump" a thread in its sorting order when it gets upvoted, you can call update() on the document with {$inc:{post_order:1}}. This will advance it by 1 in the order of result sorting. If two threads have the same value for post_order, created_at will differentiate which one comes first. So you will sort by post_order, created_at.
You will want to have an index on post_order and created_at.
Let's guess your code is the variable response (which is an array), then I would do:
response.sort(function(obj1, obj2){
return obj2.bump - obj1.bump;
});
or if you want to also take in mind name order:
response.sort(function(obj1, obj2){
var diff = obj2.bump - obj1.bump;
var nameDiff = (obj2.name > obj1.name)?-1:((obj2.name < obj1.name)?1:0);
return (diff == 0) ? nameDiff : diff;
});
Not a pleasant answer, but the solution you request is unrealistic. Here's my suggestion:
Add an OrderPosition property to your object instead of Bump.
Think of "bumping" as an event. It is best represented as an event-handler function. When an item gets "bumped" by whatever trigger in your business logic, the collection of items needs to be adjusted.
var currentOrder = this.OrderPosition
this.OrderPosition = currentOrder - bump; // moves your object up the list
// write a foreach loop here, iterating every item AFTER the items unadjusted
// order, +1 to move them all down the list one notch.
This does require iterating through many items, and I know you are trying to prevent that, but I do not think there is any other way to safely ensure the integrity of your item ordering - especially when relative to other pulled collections that occur later down the road.
I don't think a purely query-based solution is possible with your document schema (I assume you have createdDate and bump fields). Instead, I suggest a single field called sortorder to keep track of your desired retrieval order:
sortorder is initially the creation timestamp. If there are no "bumps", sorting by this field gives the correct order.
If there is a "bump," the sortorder is invalidated. So simply correct the sortorder values: each time a "bump" occurs swap the sortorder fields of the bumped document and the document directly ahead of it. This literally "bumps" the document up in the sort order.
When querying, sort by sortorder.
You can remove fields bump and createdDate if they are not used elsewhere.
As an aside, most social sites don't directly manipulate a post's display position based on its number of votes (or "bumps"). Instead, the number of votes is used to calculate a score. Then the posts are sorted and displayed by this score. In your case, you should combine createdDate and bumps into a single score that can be sorted in a query.
This site (StackOverflow.com) had a related meta discussion about how to determine "hot" questions. I think there was even a competition to come up with a new formula. The meta question also shared the formulas used by two other popular social news sites: Y Combinator Hacker News and Reddit.

Categories

Resources