Preserve JSON arrays while sorting - javascript

I have two JSON arrays coming from an external website. I sort and merge the two arrays, decode them and then sort them from highest to lowest by ID.
Currently, when the option 'alphabetical' is clicked, ?sort=alphabetical is added onto the end of the URL and when the page has finished reloading, the JSON arrays are once again decoded and merged.
This is not my desired outcome: I do not want the JSON arrays to be decoded and merged again when the option is clicked - I simply want the already decoded and merged JSON arrays to be sorted alphabetically.
Arrays:
$homepage = array();
$homepage[]= '{
"info":{
"collection":[
{
"Name":"Charlie",
"ID":"7"
},
{
"Name":"Emma",
"ID":"9"
}
]
}
}';
$homepage[] = '{
"info":{
"collection":[
{
"Name":"Bob",
"ID":"5"
}
]
}
}';
Sorting:
$data = array();
foreach ($homepage as $homepage2) {
$tmp=json_decode($homepage2, false);
$data = array_merge($data,$tmp->info->collection);
}
if(!empty($_GET['sort']) && $_GET['sort'] == 'alphabetical') {
usort($data, function ($a, $b) {
return strcmp($a->Name, $b->Name);
});
}else{
usort($data, function ($a, $b) {
return $b->ID - $a->ID;
});
}
echo'
<select onchange="location.href = this.value;">
<option value="example.php?sort=alphabetical">Alphabetical</option>
</select>
';
foreach($data as $key) {
echo'
<a href="test.com">
<p>'.$key->ID.'</p>
<p>'.$key->Name.'</p>
</a>
';
}

You could use JavaScript for doing the sorting on click, and use PHP only for passing the JSON to it.
After you provided the HTML structure you want to display the list in, I updated this answer to use div elements for the records and p elements for the fields.
We could replace the select list, for selecting the sort order, by two buttons.
Here is the PHP code:
<?php
$homepage = array();
$homepage[]= '{
"info":{
"collection":[
{
"Name":"Charlie",
"ID":"13"
},
{
"Name":"Emma",
"ID":"9"
}
]
}
}';
$homepage[] = '{
"info":{
"collection":[
{
"Name":"Bob",
"ID":"10"
}
]
}
}';
$data = array();
foreach ($homepage as $homepage2) {
$tmp=json_decode($homepage2, false);
$data = array_merge($data,$tmp->info->collection);
}
?>
<div id="container"></div>
<button id="sort1">Alphabetical</button>
<button id="sort2">High to Low</button>
<script>
var collection = <?=json_encode($data)?>;
function populate(compareFunc) {
collection.sort(compareFunc);
var container = document.getElementById('container');
container.innerHTML = '';
collection.forEach(function (key) {
var div = document.createElement("div");
div.className = "inventory";
var span = document.createElement("span");
span.textContent = key.ID;
div.appendChild(span);
span = document.createElement("span");
span.textContent = key.Name;
div.appendChild(span);
container.appendChild(div);
});
}
var populateById = populate.bind(null, function (a, b) {
return a.ID - b.ID;
});
var populateByName = populate.bind(null, function (a, b) {
return a.Name.localeCompare(b.Name);
});
document.getElementById("sort1").addEventListener('click', populateByName);
document.getElementById("sort2").addEventListener('click', populateById);
document.addEventListener('DOMContentLoaded', populateById);
</script>
For the sample data this will result in the following JavaScript/HTML, which you can test here:
var collection = [{"Name":"Charlie","ID":"13"},{"Name":"Emma","ID":"9"},{"Name":"Bob","ID":"10"}];
function populate(compareFunc) {
collection.sort(compareFunc);
var container = document.getElementById('container');
container.innerHTML = '';
collection.forEach(function (key) {
var div = document.createElement("div");
div.className = "inventory";
var span = document.createElement("span");
span.textContent = key.ID;
div.appendChild(span);
span = document.createElement("span");
span.textContent = key.Name;
div.appendChild(span);
container.appendChild(div);
});
}
var populateById = populate.bind(null, function (a, b) {
return a.ID - b.ID;
});
var populateByName = populate.bind(null, function (a, b) {
return a.Name.localeCompare(b.Name);
});
document.getElementById("sort1").addEventListener('click', populateByName);
document.getElementById("sort2").addEventListener('click', populateById);
document.addEventListener('DOMContentLoaded', populateById);
span { margin-left: 5px }
div.inventory { border-bottom: 1px solid gray }
<div id="container"></div>
<button id="sort1">Alphabetical</button>
<button id="sort2">High to Low</button>
Note that I gave the three items different ID values than in your question, since otherwise the sort order would be the same for both ID and Name.
Using tables: alternative
There are nice JavaScript libraries which give much more features to represent data sets. Here is an example using jQuery with DataTables:
var collection = [{"Name":"Charlie","ID":"13"},{"Name":"Emma","ID":"9"},{"Name":"Bob","ID":"5"}];
function populate() {
var tbody = $('#collection>tbody');
collection.forEach(function (key) {
var row = $('<tr>');
row.append($('<td>').text(key.ID));
row.append($('<td>').text(key.Name));
tbody.append(row);
});
}
$(document).ready(function(){
populate();
$('#collection').DataTable();
});
<script src="http://code.jquery.com/jquery-1.12.3.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css">
<script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js"></script>
<table id="collection">
<thead>
<tr><th>ID</th><th>Name</th></tr>
</thead>
<tbody/>
</table>
The actual code is even smaller (not counting the included library) than a pure JavaScript solution would be with a basic table. But this has sorting up and down, filtering, pagination, nice styles, ...

There are several alternatives for this answer, so I will present a simple one:
Send the data already sorted, a default one, if the user made no choices. Then, set a function that sorts the data as needed, and redraws tables/divs/whatever you are using to present them.
As an quick example:
function sortAlpha(){
for each (stuff in data){
document.getElementById("aTable").textContent=data.StringRepresentation;
}
}
Then a function for sortSize, sortEtc, etc... in each function, you clear a div content, and populate it again. This way, you do not need to request new content from servers
getElementById documentation

There are multiple solutions in which you can achieve the desired results. If you want it pure PHP way what you can do is save the data in PHP Sessions and retrive them as need.
Here is the trick comes, You create function to get the result data in which you pass single parameter whether you want to get the data from external URL or from your saved data.
Now, in your application whenever you want to refresh the saved data call the same function with parameter denoting to refresh the saved data in SESSIONS to get replaced with data from external source.
Using this method you can reuse the data you've already fetched from external source without re-fetching it every-time you reload the function.
You can make another function which will return true for all cases in which application has to re-fetch the resultset from external source.
I've written pseudo code for you to understand what I'm trying to convey,
Function check whether we've to re-fetch the result from external source:
function hasToRefreshResult() {
if(/* CERTAIN CONDITIONS */) {
return true;
}
return false;
}
Couple of functions to get the data from local/external source according to the parameter passed:
function getResultArray($getdatafromlocal) {
if(!hasToRefreshResult() && $getdatafromlocal && array_key_exists("filertereddata",$_SESSION) && isset($_SESSION["filertereddata"])) {
$data=$_SESSION["filertereddata"];
} else {
$data=getDataFromExternalURL();
}
if(!empty($_GET['sort']) && $_GET['sort'] == 'alphabetical') {
usort($data, function ($a, $b) {
return strcmp($a->Name, $b->Name);
});
} else {
usort($data, function ($a, $b) {
return $b->ID - $a->ID;
});
}
return $data;
}
function getDataFromExternalURL() {
/*****
YOUR LOGIC TO GET DATA FROM EXTERNAL URL;
*****/
$data = array();
foreach ($homepage as $homepage2) {
$tmp=json_decode($homepage2, false);
$data = array_merge($data,$tmp->info->collection);
}
$_SESSION["filertereddata"]=$data;
return $data;
}
I hope this will solve your issue strictly using PHP.
Also don't forget the write session_start(); at the top of the PHP file you will be using this functions.

Related

How can I grab a div in PHP that's in a loop within a javascript function

Below is the code.
under the function there is a resultContainer.innerHTML that populates a list of QR codes scanned. How can $_POST the values in PHP so that I can send it in an email format? I tried adding a name within the div (<div **name="qrOutput"**>[${countResults}] - ${qrCodeMessage}</div>) but PHP does not pick it up. Only returns an empty string.
I also tried giving the <div id="qr-reader-results"></div> element a name but because the output is within another div inside this div I also got an empty result.
Thanks a lot for any help.
<!-- start -->
<div id="qr-reader" style="width:500px"></div>
<div id="qr-reader-results"></div>
<div id="root"></div>
<script>
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" ||
document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(function() {
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, countResults = 0;
function onScanSuccess(qrCodeMessage) {
if (qrCodeMessage !== lastResult) {
++countResults;
lastResult = qrCodeMessage;
resultContainer.innerHTML += ***`<div">[${countResults}] - ${qrCodeMessage}</div>`;***
}
}
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", {
fps: 10,
qrbox: 250
});
html5QrcodeScanner.render(onScanSuccess);
});
</script>
<p id="QRout"></p>
You can store your results in an other variable when you add it to the DOM.
Declare a variable to store your results
var myResults = [];
When you add the result to the DOM add also the results in array variable
// ...
resultContainer.innerHTML += `<div>[${countResults}] - ${qrCodeMessage}</div>`;
myResults.push({count: countResults, message: qrCodeMessage})
Then you can use myResult var on a POST request
myCustomPostFunction('/yourUrl/', myResult);
The "myCustomPostFunction" will depend on the way you want to send the data
Check this codepen: https://codepen.io/zecka/pen/VwKNpze
Post request like a form submit
If you want to send the data to the current page like a form post, you can find an example here: https://stackoverflow.com/a/133997/2838586
Post request to a rest api
Fetch: POST json data

How to display array elements passed into Handlebars file one by one on button click?

Suppose, I have following array of objects successfully sent to handlebars file from my express server:
[
{
"name": "Jon",
"isPositive": true
},
{
"name": "Cercei",
"isPositive": false
}
]
Each time when my button gets clicked, I want to show next element of the array on my view. Is it possible with handlebars? Or do I need something more complex like Vue.js to solve this problem? Here is simple pseudocode I would like to achieve:
<head>
<script>
$(document).ready(() => {
var index = 0;
var currentCharacter = getCurrentCharacter();
start();
});
</script>
</head>
<body>
<h3 id="name"></h3>
<p id="someInfo"> </p>
<button onclick="next()">Next</button>
<script>
function getCurrentCharacter() {
/// somehow access array of objects sent from server
var myCharacters = {{characters}}; // {} used because of handlebars
var character = myCharacters[index];
index += 1;
return character;
}
function start() {
document.getElementById("name").textContent = currentCharacter.name;
/// as you see here, I change my UI based on current object fields/attributes
if (currentCharacter.isPositive) {
document.getElementById("someInfo").style.display = "block";
document.getElementById("someInfo").textContent = "Some text here";
} else {
document.getElementById("someInfo").style.visibility = "hidden";
}
}
function next() {
currentCharacter = getCurrentCharacter();
start();
}
</script>
</body>
Is it possible to have such a logic with handlebars or other templating languages? If they are not build for these purposes, please recommend me another way to solve this problem.

Real time Javascript filtering not working throws undefined [duplicate]

This question already exists:
My live string search always returns false when it tries to match with includes() [duplicate]
Closed 3 years ago.
I want the code to return items from the JSON when i write something in the search bar (getting data from JSON tested and working).
The code is supposed to go to the JSON, read the line of the JSON (object) and compare with the keyword to see if it contains the keyword. If it contains it will display in <li> the item. It is always throwing "undefined".
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">
</head>
<body>
<input type="text" placeholder="Search Users" id="filter_items"/>
<ul id="items-list">
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
var json = (function() {
var json = null;
$.ajax({
'async': false,
'global': false,
'crossDomain': true,
'method': "get",
'url': "products.json",
'dataType': "json",
'success': function (data) {
json = data;
}
});
return json;
})();
// lets filters it
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = function(event, json){
keyword = input.value.toLowerCase();
var li = "";
for (var index in json)
{
for (var j in json[index])
{
var line = json[index][j];
var filtered_items = line.title.filter(function(lin){
lin = lin.title.toLowerCase();
return lin.title.indexOf(keyword) > -1;
});
}
}
li += "<li>"+filtered_items+"</li>";
ul.innerHTML = li;
}
input.addEventListener('keyup', filterItems);
</script>
</body>
</html>
The JSON
{
"items": [{
"title": "Express"
}, {
"title": "Unexpress"
}]
}
It is supposed to return items matching the keyword in real time.
UPDATE
JSON isn't passing as parameter into the function with event as parameter too. Anyone know how to solve it?
You aren't passing an additional variable to the function when you trigger the event, so it's unclear why you'd expect json to be populated.
But you don't need it anyway - the content of JSON doesn't change every time the event happens, the data is always the same. Just download it when your page loads, and then add the event listener when it's finished downloading, and use the function without the additional parameter. json can be declared global so it'll be in scope.
I think the code might make more sense like this:
var json = null;
$.ajax({
'global': false,
'crossDomain': true,
'method': "get",
'url': "products.json",
'dataType': "json",
'success': function (data) {
json = data;
input.addEventListener('keyup', filterItems);
}
});
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = function(event){
keyword = input.value.toLowerCase();
var li = "";
for (var index in json)
{
for (var j in json[index])
{
var line = json[index][j];
var filtered_items = line.title.filter(function(lin){
lin = lin.title.toLowerCase();
return lin.title.indexOf(keyword) > -1;
});
}
}
li += "<li>"+filtered_items+"</li>";
ul.innerHTML = li;
}
P.S. 'async': false is deprecated due to the poor user experience it creates (locking up the main browser UI during requests). Some browsers will issue a console warning when you try to use it. But there should be no need for it. You should aim to use callbacks/promises correctly instead. I removed it in my example above, and instead (for reliability) we don't add the event listener to the input box until the JSON download has completed. Hopefully it isn't too big a file.
P.P.S. If you ever find yourself swapping the static JSON file for a server-side script to retrive product data from a database, it would be advisable to change your code so that the filtering of data happens on the server - usually it's a lot more efficient to filter using a SQL query and return only the data which is truly needed, than to download everything and then filter it using JavaScript.
I belive the filtering part should be changed.
Maybe to something like the following?
var json = { "items": [{"title": "Express"}, { "title": "Unexpress"}] };
// lets filters it
ul = document.getElementById("items-list");
input = document.getElementById('filter_items');
var filterItems = (event) => {
keyword = input.value.toLowerCase();
var li = "";
var filtered_items = json.items.filter(function(lin){
return lin.title.toLowerCase() == keyword;
});
for ( var i in filtered_items ) {
li += "<li>"+filtered_items[i].title+"</li>";
}
ul.innerHTML = li;
}
input.addEventListener('keyup', filterItems);
<body>
<input type="text" placeholder="Search Users" id="filter_items"/>
<ul id="items-list">
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

Image OnClick to Javascript function

I am making a registration page that allows you to register an account to a mysql database for my uni project.
In this page you can also 'select' your avatar picture. Here is the code below:
<u>Select your avatar:</u><br>
<?php
// open this directory
$image_dir = opendir("images/avatars");
// get each entry
while( $image = readdir( $image_dir ) )
{
$dirArray[] = $image;
}
// close directory
closedir($image_dir);
// count elements in array
$indexCount = count($dirArray);
// loop through the array of files and print them all in a list
for($index=0; $index < $indexCount; $index++)
{
$extension = substr($dirArray[$index], -3);
if( $extension == "jpg" || $extension == "gif" )
{
//echo("<a href='#'>");
echo("<img id='$index' onClick='SetAvatar($index)' img src='images/avatars/$dirArray[$index]' class='o'> ");
//echo("</a>");
}
}
?>
<script>
function SetAvatar(id) {
var image = document.getElementById(id);
if( CurSelectedImage != null && id != CurSelectedImage )
{
var image_to_unselect = document.getElementById(CurSelectedImage);
image_to_unselect.Selected = false;
image_to_unselect.style.border = null;
}
if( image.Selected != true )
{
image.style.border = 'medium solid blue';
image.Selected = true;
SelectedImage = id;
}
else
{
image.style.border = null;
image.Selected = false;
SelectedImage = null;
}
}
</script>
This selects the avatar picture, makes the border blue and stores the selected image id in a variable but how would I pass the variable with the selected image id back to php so I can save it??
Thanks
can you show your CSS codes too ?
or you can find here jQuery select and unselect image
your answer
You have to think about your application design. Mixing PHP and Javascript isn't the best idea. Use a API instead of mixing code. You can call this API with Ajax. I think this is a good design choice:
Create a getImages API in PHP: You output the data in a json array.
You calling this api with javascript and generating the DOM with the json
You creating a click handler in javascript and calling again a API in PHP
You getting the json data in PHP and saving it in your db
:)
My suggestion would be to use a CSS class. Remove any existing instances of the class then add the class to the selected image.
function SetAvatar(id) {
//remove existing border(s) a while loop is used since elements is a live node list which causes issues with a traditional for loop
var elements = document.getElementsByClassName("selected-avatar");
while (elements.length > 0) {
var element = elements.item(0);
element.className = element.className.replace(/(?:^|\s)selected-avatar(?!\S)/g , '');
}
var image = document.getElementById(id);
image.className += " selected-avatar";
}
To pass the avatar value, I'd suggest using a form to pass all of your registration data to process_register (if you are not already). You can add an input of type hidden to your form and populate the value through javascript on submit.
html:
<form id="registration-form" action="process_register.php" method="put">
<input id="registration-avatar" type="hidden" />
<button id="registration-submit">Submit</button>
</form>
javascript:
document.getElementById("registration-submit").onclick = function(){
var avatarValue; //you'll need to write some code to populate the avatarValue based on the selected avatar image
document.getElementById("registration-avatar").value = avatarValue;
document.getElementById('registration-form').submit();
};
Then in php you can get the values using $_POST
http://php.net/manual/en/reserved.variables.post.php

Storing selected row in array in JavaScript

How do we stor selected row in array in JavaScript?
You shoud be more specific in what kind of object youre using in your html (DOM) code.
exampl:
if you're using a SELECT 'resources' in a form
var fl = form.resources.length -1;
//Pull selected resources and add them to list
for (fl; fl > -1; fl--) {
if (form.resources.options[fl].selected) {
theText = form.resources.options[fl].text;
theValue = form.resources.options[fl].value);
//your code to store in an aray goes here
//...
}
}
If I got it you want to store selected table rows using javaScript.
If yes, this may help.
This piece of code is to accumulate selected ID's (dataId), based on selection of selected row's id(elemId), in an input (hidId).
I have modified my original code to maintain the records in an Array as well.
function checkSelection(hidId,elemId,dataId)
{
var arr =
str = '_' + dataId + '_';
hid = document.getElementById(hidId);
row = document.getElementById(elemId);
if(!row.classList.contains("selected")) {
row.classList.add("selected");
if(!hid.value.toString().includes(str)) {
hid.value = hid.value + str;
}
if(arr.includes(dataId))
arr.push(dataId);
}
else {
row.classList.remove("selected");
if(hid.value.toString().includes(str))
hid.value = hid.value.replace(str,"");
if(!arr.indexOf(dataId)==-1)
delete arr[arr.indexOf(dataId)];
}
alert(arr.toString());
}[I have tested it][1]
to focus more on the Array() a basic solution would be as below:
function checkSelect(hidId,elemId,dataId)
{
row = document.getElementById(elemId);
str = "";
if(document.getElementById(hidId).getAttribute("value")!=null)
str = str+document.getElementById(hidId).getAttribute("value");
str= str.replace('[','')
.replace(']','')
.replace('"','')
.replace('\\','');
document.getElementById(hidId).setAttribute("value",str);
alert(document.getElementById(hidId).value);
var arr = new Array();
if(document.getElementById(hidId).value.length!=0 ) {
arr=document.getElementById(hidId).value.split(',');
}
if(!row.classList.contains("selected")) {
row.classList.add("selected");
if(!arr.includes(dataId.toString())) {
arr.push(dataId.toString());
}
}
else {
row.classList.remove("selected");
if(arr.includes(dataId.toString()))
{
delete dataId.toString();
arr.splice(arr.indexOf(dataId.toString()),1)
}
}
if(arr.length>0) {
document.getElementById(hidId).setAttribute("value", arr.toString());
}
else
document.getElementById(hidId).setAttribute("value", "");
}

Categories