What I am trying to do is given a list of objects responseList<Object> in which the objects have a date field on string format "2015-01-16", filter the list to only get objects form last month or the current year and add the filtered objects to a new list.
The first thing that I have done is convert the string into date and the result is "Fri Jan 16 00:00:00 GMT-05:00 2015".
using:
val dateformated = getFormatedDate(object.date)
val cal = Calendar.getInstance()
cal.time = dateFormated; //date formatted is the string converted to date
var responseMonth = cal.get(Calendar.MONTH)
var responseYear = cal.get(Calendar.YEAR)
I am able to retrieve both month and year of the response and using this:
val now: LocalDate = LocalDate.now()
val lastMonth = now.minusMonths(1).month
val thisYear = now.year
I get the current year and month
What I am failing to is filtering the object list based on the requirement to create another list.
Any help or suggestions would be great, thanks
#Test
fun filterDateListByLastMonthOrThisYear() {
val fakeDateOne = LocalDate.of(2023, 2, 1)
val fakeDateTwo = LocalDate.of(2020, 3, 12)
val fakeDateThree = LocalDate.of(1999, 12, 31)
val fakeDateFour = LocalDate.of(2023, 2, 2)
val fakeDateFive = LocalDate.of(2023, 1, 3)
val datesList = listOf(
fakeDateOne,
fakeDateTwo,
fakeDateThree,
fakeDateFour,
fakeDateFive
)
val today = LocalDate.now()
val lastMonth = today.month.minus(1)
val thisYear = today.year
val expected = listOf(fakeDateOne, fakeDateFour, fakeDateFive)
val actual = datesList.filter { it.year == thisYear || it.month == lastMonth }
assertEquals(expected, actual)
}
I believe this question and answer explains how to format time series data into readable date labels in Java. How do you do the same thing in Kotlin?
You could create a custom formatter class extending the IAxisValueFormatter:
class MyCustomFormatter() : IAxisValueFormatter
{
override fun getFormattedValue(value: Float, axis: AxisBase?): String
{
val dateInMillis = value.toLong()
val date = Calendar.getInstance().apply {
timeInMillis = dateInMillis
}.time
return SimpleDateFormat("dd MMM", Locale.getDefault()).format(date)
}
}
Then assign it to your chart with
chart?.xAxis?.valueFormatter = MyCustomFormatter()
Using version 3.0+ of the MPAndroidChart:
Set formatter to the x axis:
// Formatter to adjust epoch time to readable date
lineChart.xAxis.valueFormatter = LineChartXAxisValueFormatter()
Create a new class LineChartXAxisValueFormatter:
class LineChartXAxisValueFormatter : IndexAxisValueFormatter() {
override fun getFormattedValue(value: Float): String? {
// Convert float value to date string
// Convert from seconds back to milliseconds to format time to show to the user
val emissionsMilliSince1970Time = value.toLong() * 1000
// Show time in local version
val timeMilliseconds = Date(emissionsMilliSince1970Time)
val dateTimeFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
return dateTimeFormat.format(timeMilliseconds)
}
}
When the entries are added to the ChartDataArray they are added in seconds, not milliseconds, to avoid potential precision issues with inputting as a float (i.e. milliseconds divided by 1000).
chartDataArray.add(Entry(secondsSince1970.toFloat(), yValue.toFloat()))
Happy coding!
I have a LineChart in my app. The x value of the data points I use are milliseconds that I display as a date using ValueFormatter. This works fine, except I only want the date to be displayed once. I tried the following code, and it almost works, however it only works for the first date displayed. Any help appreciated!
Here is an example of my problem:
This is my most successful code, I use kotlin:
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
val dateFormatter = SimpleDateFormat("dd/MM")
var lastDate = ""
val formatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
val date = dateFormatter.format(Date(value.toLong()))
if (date==lastDate){
return ""
}
lastDate = date
return date
}
}
xAxis.valueFormatter = formatter
The following is the whole function, the json is temporary and is only there for testing purposes, the app will receive similar jsons.
private fun chart(){
val json = JSONObject("""{"payload":{"serie":{"data":
[[1564129484189,3287],[1564129933626,3287],[1564130385185,3287],[1564130834622,3280],[1564131284854,3280],[1564131736401,3280],
[1564132184652,3280],[1564132634092,3280],[1564133083925,3280],[1564133534963,3287],[1564133984117,3287],[1564134434677,3287],
[1564134883854,3280],[1564135333830,3280],[1564135785080,3280],[1564136234991,3280],[1564136685793,3280],[1564137134667,3280],
[1564137584570,3280],[1564138034367,3280],[1564138484236,3280],[1564138934104,3273],[1564139384690,3273],[1564139834549,3273],
[1564140284417,3280],[1564140734298,3280],[1564141183544,3280],[1564141634006,3273],[1564142083871,3273],[1564142533644,3273],
[1564142984818,3273],[1564143433797,3273],[1564143883681,3273],[1564144334485,3273],[1564144785207,3273],[1564145234378,3273],
[1564145685180,3280],[1564146134070,3280],[1564146584966,3280],[1564147034886,3280],[1564147484703,3280],[1564147934612,3280],
[1564148384456,3280],[1564148834343,3280],[1564149283943,3280],[1564149734825,3280],[1564150184715,3280],[1564150634494,3280],
[1564151084377,3287],[1564151534299,3287],[1564151984188,3287],[1564215691264,3287],[1564216142234,3287],[1564216591762,3287],
[1564217041687,3287],[1564217491103,3287],[1564217941061,3287],[1564218391529,3287],[1564218841469,3287],[1564219291586,3287],
[1564219741426,3287],[1564220192557,3287],[1564220641356,3287],[1564221091465,3280],[1564221541440,3280],[1564221991829,3280],
[1564222441313,3287],[1564222893010,3287],[1564223344202,3287],[1564223792494,3280],[1564224696695,3280],[1564225144701,3280],
[1564225591575,3280],[1564226047071,3280],[1564226492027,3280],[1564226951626,3280],[1564227392573,3280],[1564227841277,3280],
[1564228292385,3287],[1564228741844,3287],[1564229192885,3287],[1564229642831,3287],[1564230092311,3287],[1564230550690,3287],
[1564230993205,3287],[1564231443101,3287],[1564231892541,3287],[1564232344078,3280],[1564232793143,3280],[1564233255081,3280]]}}}""")
val payload = json.get("payload") as JSONObject
val serie = payload.get("serie") as JSONObject
val dataPoints = serie.get("data") as JSONArray
val values = arrayListOf<Entry>()
for (i in 0 until dataPoints.length()) {
val innerData = dataPoints[i] as JSONArray
values.add (Entry(
innerData[0].toString().toFloat() ,
innerData[1].toString().toFloat()
))
}
val chart = findViewById<LineChart>(R.id.graph2)
chart.setTouchEnabled(true)
chart.setPinchZoom(true)
chart.isScaleYEnabled = false
val dataSet = LineDataSet(values, "Service")
dataSet.color = ContextCompat.getColor(this, R.color.colorAccent)
dataSet.setCircleColor(ContextCompat.getColor(this, R.color.colorPrimaryDark))
dataSet.setDrawValues(false)
dataSet.lineWidth = 2f
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
val dateFormatter = SimpleDateFormat("dd/MM")
var lastDate = ""
val formatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
val date = dateFormatter.format(Date(value.toLong()))
if (date==lastDate){
return ""
}
lastDate = date
return date
}
}
xAxis.valueFormatter = formatter
val yAxisRight = chart.axisRight
yAxisRight.isEnabled = false
val yAxisLeft = chart.axisLeft
yAxisLeft.granularity = 1f
val desc = Description()
desc.text = "custom description"
chart.description = desc
val data = LineData(dataSet)
chart.data = data
chart.animateX(1000)
chart.invalidate()
}
I don't know in kotlin but in java or for some idea you can refer to my answer in the given link- DateTime axis in MPAndroidChart
I'm attaching the code in which I've used IndexAxisValueFormatter. Also declare dev String array in your class final String dev[] = new String[100]; and use it as in below code.
for (int i = 0; i < jsonArray1.length(); i++) {
JSONObject object1 = jsonArray1.getJSONObject(i);
dev[i] = object1.getString("block_time"); //for xaxis needed
XAxis bottomAxis = lineChart.getXAxis();
bottomAxis.setValueFormatter(new IndexAxisValueFormatter(dev));/*for x axis values*/
bottomAxis.setLabelCount(lineEntries.size());
bottomAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
bottomAxis.setDrawLabels(true); //to hide all xaxis values
bottomAxis.setDrawGridLines(false);
bottomAxis.setDrawAxisLine(true);}
I have a code to display a data in a line graph and I need to change the xAxis to date but the log have a error
java.lang.NumberFormatException: For input string: "2019-04-19 00:00:00"
Below is my code snippet for line graph
mLineChart = findViewById(R.id.chart)
importData()
val dataSet = LineDataSet(entries, "Water")
val lineData = LineData(dataSet)
mLineChart.axisRight.isEnabled = false
mLineChart.data = lineData
dataSet.color = Color.RED
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER
mLineChart.description.text = ""
mLineChart.legend.isEnabled = false
mLineChart.invalidate()
val leftAxis = mLineChart.axisLeft
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
leftAxis.axisMinimum = 0f
leftAxis.axisMaximum = 30f
val rightAxis = mLineChart.axisRight
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
rightAxis.axisMinimum = 0f
rightAxis.axisMaximum = 30f
var xAxis = mLineChart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setDrawGridLines(false)
xAxis.axisMinimum = 0f
xAxis.labelCount = 5
xAxis.axisMaximum = 400f
xAxis.granularity = 1f
xAxis.isGranularityEnabled = true
xAxis.valueFormatter = DateAxisValueFormatter(null)
Below is my code snippet for inserting data
private fun importData() {
for (data in dataList) {
val date = java.lang.Float.parseFloat(data.date)
val water = java.lang.Float.parseFloat(data.water)
entries.add(Entry(date, water))
}
Below is my code snippet for value formatter
internal inner class DateAxisValueFormatter(private val mValues: Array<String>?) : ValueFormatter(),
IAxisValueFormatter {
var sdf = SimpleDateFormat("hh:mm", Locale.ENGLISH)
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return sdf.format(Date(value.toLong()))
}
}
Your exception is clear, you're trying to convert a string into Float, but this string is not a Number.
If you want to display a date, you have to format your date in milliseconds before.
Try something like that:
private fun importData() {
val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
for (data in dataList) {
val date = dateFormatter.parse(data.date)
val water = java.lang.Float.parseFloat(data.water)
//Here you get your date as a Long and you convert it in Float
entries.add(Entry(date.time.toFloat(), water))
}
EDIT:
In your xAxis set your own valueFormatter this way to convert your float back in date:
val x = chart.xAxis
x.setValueFormatter { value, _ ->
SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).format(value.toLong())
}