Sub total text list when first 10 chars change

I have a text file with lines as follows

27/04/2025 41 Some description here Spain Granada Granada
27/04/2025 02 Some description here Spain Nerja Malaga
27/04/2025 02 Some description here Spain Nerja Malaga
26/04/2025 22 Some description here United States Boydton Virginia
26/04/2025 07 Some description here United States Columbus Ohio
26/04/2025 39 Some description here United States Columbus Ohio
26/04/2025 23 Some description here United States
25/04/2025 37 Some description here Spain Málaga Malaga
25/04/2025 37 Some description here Spain Málaga Malaga

The first 10 characters are always in the same position as a text date, I want to count the lines and break when the first 10 characters change, then display a total for each date as follows

27/04/2025 41 Some description here Spain Granada Granada
27/04/2025 02 Some description here Spain Nerja Malaga
27/04/2025 02 Some description here Spain Nerja Malaga
Total Clicks for date 27/04/2025 = 3
26/04/2025 22 Some description here United States Boydton Virginia
26/04/2025 07 Some description here United States Columbus Ohio
26/04/2025 39 Some description here United States Columbus Ohio
26/04/2025 23 Some description here United States
Total Clicks for date 267/04/2025 = 4
25/04/2025 37 Some description here Spain Málaga Malaga
25/04/2025 37 Some description here Spain Málaga Malaga
Total Clicks for date 25/04/2025 = 2

How big does this text file typically get and for what length of time do you keep the data?

Are you appending the data to this file in php code or is the web server producing this?

What code have you tried?

I guess I’ll repeat this from one of your earliest threads - you should be storing this data in a database. Doing so will make producing these different reports extremely easy.

Since you are storing this data in a text file, you MUST use file locking and have error handling for the file operations to prevent your text file from being truncated during concurrent accesses. Using a database will solve this since the database performs the necessary table locking so that concurrent operations will not interfere with each other.

For your current question/problem, if the lines you have posted are in a file, what does the code - ‘to collect the click lines into groups’, have to do with the problem?

I would read the lines from the file into an array, loop over the array to index/pivot the data using the date as the main array index, then to produce the output, loop over the now date-indexed data, loop over and display the lines for the current date, get the count() of the number of elements for the current date, then display the date/count output.

File locking is not about security, it is about controlling access to a file for reading and writing so that data is not corrupted by concurrent access to the same file. See this link - PHP: flock - Manual

Here’s some copy/paste code showing how to produce the date/counts -

<?php

// name of the text file holding the lines you have posted
$file = 'data.txt';

// example 27/04/2025 41 Some description here Spain Granada Granada

// read the lines from the file into an array
$lines = file($file,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);

if(!$lines)
{
	die('could not read file or no data in file');
}

// index/pivot the data using the date as the main array index
$data = [];
foreach($lines as $line)
{
	$date = substr($line, 0, 10);
	$data[$date][] = $line;
}

// produce the output
// loop over the now date-indexed data
foreach($data as $date=>$lines)
{
	// loop over and display the lines for the current date
	foreach($lines as $line)
	{
		echo "$line<br>";
	}
	// get the count() of the number of elements for the current date
	$total = count($lines);
	// display the date/count output
	echo "Total Clicks for date $date = $total<br>";
}

You can get this slightly faster and less memory consuming by changing

$data[$date][] = $line;

to

$data[$date][] = 1;

The content of the line is irrelevant for the later counting.

You could as well count directly in the first loop like this:

if (!isset($data[$date])) $data[$date]=0;
$data[$date]++;

which will store a 0 in the array element first, if there isn’t any value and will count the value one up for each matching line (including the first one)


$xdata = [];
$ydata = [];
foreach ($occurences as $x=>$y) {
  $xdata[] = "\"$x\"";
  $ydata[] = $y;
}

echo "const xArray = [" . implode(",",$xdata) . "];" . PHP_EOL;
echo "const yArray = [" . implode(",",$ydata) . "];" . PHP_EOL;

this will output your 2 lines of JavaScript in the browser.
So place it in your script block at the right position.

how do I echo the array into the chart html (in the same script) I am trying this in
php

const xArray = [" . implode(",",$xdata) . "]; . PHP_EOL;
const yArray = [" . implode(",",$ydata) . "]; . PHP_EOL;

then what do I write in html in the same script to echo this?

Inside the html document, it’s customary to only echo the php produced output. If you use php’s short-open-print tag and leave out the ; right before a closing php tag, you can use this syntax - <?=some php produced output?>

This is your original markup in the html document -

You would simply change it to this -

const xArray = [<?=implode(",",$xdata)?>];
const yArray = [<?=implode(",",$ydata)?>];

Sorry I just cannot get this to work, here is the section of the code

sort($data);
function isoDate($text)
{
    $d = substr($text, 0, 10);
    return DateTime::createFromFormat('d/m/Y', $d)->format('Y-m-d');
}
$occurences = array_count_values($data);
$xdata = [];
$ydata = [];
foreach ($occurences as $x=>$y) {
  $xdata[] = "\"$x\"";
  $ydata[] = $y;
}
echo "const xArray = [" . implode(",",$xdata) . "];" . PHP_EOL;
echo "const yArray = [" . implode(",",$ydata) . "];" . PHP_EOL;
?>

<!DOCTYPE html>
<html>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<body>

<div id="myPlot" style="width:100%;max-width:700px"></div>

<script>
const xdata = [<?=implode(",",$xdata)?>];
const ydata = [<?=implode(",",$ydata)?>];

const data = [{
  x:xArray,
  y:yArray,
  type:"bar",
  orientation:"v",
  marker: {color:"rgba(0,0,255,0.6)"}
}];
const layout = {title:"Graph of Daily Clicks as at <? echo $Date; ?>"};
Plotly.newPlot("myPlot", data, layout);
</script>

</body>
</html>
<?php
//retrieve last tot count

obviously if I enter the result of your two echo lines into the html it works perfectly
const xArray = ["11/04/2025 ","12/04/2025 ","13/04/2025 ","14/04/2025 ","15/04/2025 ","16/04/2025 ","17/04/2025 ","18/04/2025 ","19/04/2025 ","20/04/2025 ","21/04/2025 ","22/04/2025 ","18/04/2025 ","23/04/2025 ","24/04/2025 ","25/04/2025 ","26/04/2025 ","27/04/2025 ","28/04/2025 ","29/04/2025 "];
const yArray = [1,122,66,6,68,8,4,1,1,5,3,5,32,5,6,48,13,2,1,1];
but I want the chart to display from your loop without any intervention

You are echoing the result twice, which breaks your page.

You shouldn’t just copy & paste without trying to understand what it actually does.

The echos in my example should be removed since you are following phdr for the output.

In addition you have messed up your JavaScript by putting

const xdata = [<?=implode(",",$xdata)?>]; const ydata = [<?=implode(",",$ydata)?>]

instead of

const xArray = [<?=implode(",",$xdata)?>]; const yArray = [<?=implode(",",$ydata)?>]

You don’t need any manual intervention, because the output happens exactly where it should, but you must usevthe correct names ofc.

Yes you are right I am not understanding, so now we have this

$occurences = array_count_values($data);
$xdata = [];
$ydata = [];
foreach ($occurences as $x=>$y) {
$xdata[] = “”$x"";
$ydata[] = $y;
}
What should go here?
“const xArray = [” . implode(",",$xdata) . “];” . PHP_EOL;
“const yArray = [” . implode(",",$ydata) . “];” . PHP_EOL;
?>

<!DOCTYPE html>
<html>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<body>

<div id="myPlot" style="width:100%;max-width:700px"></div>

<script>
const xArray = [<?=implode(",",$xdata)?>]; 
const yArray = [<?=implode(",",$ydata)?>];

nothing

don’t echo, if you don’t want to echo

sort($data);
function isoDate($text)
{
    $d = substr($text, 0, 10);
    return DateTime::createFromFormat('d/m/Y', $d)->format('Y-m-d');
}
$occurences = array_count_values($data);
$xdata = [];
$ydata = [];
foreach ($occurences as $x=>$y) {
  $xdata[] = "\"$x\"";
  $ydata[] = $y;
}
?>
<!DOCTYPE html>
<html>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<body>

<div id="myPlot" style="width:100%;max-width:700px"></div>

<script>
const xArray = [<?=implode(",",$xdata)?>];
const yArray = [<?=implode(",",$ydata)?>];

const data = [{
  x:xArray,
  y:yArray,
  type:"bar",
  orientation:"v",
  marker: {color:"rgba(0,0,255,0.6)"}
}];
const layout = {title:"Graph of Daily Clicks as at <? echo $Date; ?>"};
Plotly.newPlot("myPlot", data, layout);
</script>

</body>
</html>
<?php
//retrieve last tot count

PS: I can explain it to you, but I cannot understand it for you. This is your job. Otherwise you may wanna use paid services to have someone else write your code.

with nothing there I don’t get a graph

$occurences = array_count_values($data);
$xdata = [];
$ydata = [];
foreach ($occurences as $x=>$y) {
  $xdata[] = "\"$x\"";
  $ydata[] = $y;
}
    
?>

<!DOCTYPE html>
<html>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<body>

<div id="myPlot" style="width:100%;max-width:700px"></div>

<script>
const xArray = [<?=implode(",",$xdata)?>]; 
const yArray = [<?=implode(",",$ydata)?>];

const data = [{
  x:xArray,
  y:yArray,
  type:"bar",
  orientation:"v",
  marker: {color:"rgba(0,0,255,0.6)"}
}];

Did you save the file after making any changes? Did you force a reload of the page in your browser after making any changes?

Did you cut off the bottom of the actual code?

When you do a ‘view source’ of the page in your browser what do you get? In the browser’s developer tools, console tab, are there any errors?

You have new-line characters in with the data, after the 1st piece of data.

When you echo the data and copy/paste it, the new-line characters do not get copied (they exist in the ‘view source’ of the output).

If you cannot find where the new-lines are coming from, you will need to post your actual code.

seems there is a bug in array_count_values

This is part of our efforts to get Drupal 8 green on PHP 7, see https://www.drupal.org/node/2454439.

This might be related to PHP :: Bug #69371 :: Hash table collision leads to inaccessible array keys, which was a somewhat similar php bug that we found (Similar as in, arrays/array functions behaving strangely).

We have a code line like this:

array_count_values($form_state->getValue(‘prefix’))

This displays:
array_count_values(): Can only count STRING and INTEGER values! Warning NegotiationUrlForm.php 183 Drupal\language\Form\NegotiationUrlForm->validateForm()

But the array definitely is valid:
Array
(
[en] =>
[fr] => fr
)

No. The problem is that your data has new-line characters as part of the date, after the 1st value, that using trim() will probably fix or if you are reading this data from a text file, you need to remove (ignore) the new-line characters when you read the file.

Geat, it was the newline that fixed it thanks so much for all your help

Sponsor our Newsletter | Privacy Policy | Terms of Service