Append an element before 1st key inside .map() function - javascript

I want to recreate whatsapp/telegram-like timeline where messages divided by days.
For rendering messages in React I use Object.key(messages).map function.
Object.keys(this.messages).map(e => {
return <div key={i++}>
{ this.messages[e] }
How to add e.g. 'today' between the last yesterday and first today messages?

I would group all messages into separate arrays, each for each day that occurs. In order to do that, create an object - its keys will be unique days and its values - messages from these days.
// object similar to your 'messages' state
const messages = {
message1: {
body: "one day before message",
time: 1534433188201
},
message2: {
body: "newest message",
time: 1534519588201
},
message3: {
body: "2 days before newest message",
time: 1534346788201
},
message4: {
body: "also 2 days before newest message",
time: 1534346788250
}
};
// creating array from your object
const messagesArray = Object.keys(messages).map(m => messages[m]);
// sorting array - oldest to newest
const latestMessages = messagesArray.sort((a, b) => a.time > b.time);
// grouping by date - create an object, each key is a different date and value is an array of messages from that day
const groupedByDate = {};
latestMessages.forEach(message => {
const date = new Date(message.time);
const day = date.getDate();
const month = date.getMonth();
const year = date.getFullYear();
// this will create 'date_17-7-2018' format as an example - you can do whatever you want/need here
const key = `date_${day}-${month}-${year}`;
// push message to existing key or create new array containing this message
if(groupedByDate[key]) {
groupedByDate[key].push(message);
} else {
groupedByDate[key] = [message];
}
});
console.log(groupedByDate);
The rendering part seems easy now - here's an example how i would approach this:
Map over Object.keys(groupedByDate) and for each key return div or span with a className="date-label"(example). If a day extracted from this key is equal to (new Date()).getDate() - render "today", if it's (new Date()).getDate() - 1 - render "yesterday", otherwise render "X days ago". Now inside this map loop you also need to map over groupedByDate[key] (array of messages from this day) and render messages.

Object.keys(this.messages).map(message => {
const date = new Date();
date.setHours(0,0,0,0); // will set to midnight
const today = Math.round(date.getTime() / 1000);
const timeDifference = this.state.time - today;
const isInRange = timeDifference >= 0 && timeDifference <=86400;
if (isInRange && !this.state.labelAlreadyPresent) {
this.setState({ showLabel: true, labelAlreadyPresent: true });
} else if(!isInRange) {
this.setState({ labelAlreadyPresent: false, showLabel: true });
} else {
this.setState({ showLabel: false });
}
return (
<div key={message.time}>
{this.state.showLabel && <label> Today </label>}
{ message.body }
</div>
);
so basically you first get today's date and set it to midnight. then you get the unix timestamp in seconds. After that you compare the timestamp with the timestamp you received in response and if its within the range of 0 to 86400(equal to 1 day) then you show the Today label.
In the initial state make labelAlreadyPresent and showLabel as false

Related

Query paginated API endpoint from end to the very first item by providing start and endtime parameters

I am having big difficulties in getting this done so I have to ask you guys for help now.
I am hitting the binance API endpoint for historical futurerates and I need all available data for every symbol in my array (from current to the very first created item).
The API offers query parameters and limits to do so "startTime" and "endTime" of type long timestamp in ms.
Here is the link to the docs
[fundingrate api endpoint[1]
[1]: https://binance-docs.github.io/apidocs/futures/en/#get-funding-rate-history
Using my approach, I am getting a bunch of results, but they randomly stop at some point in the past, so my pagination logic must be wrong. I just can not seem to find out where.
This is my current code
(async () => {
try {
const markets = symbols;
const targetDate = moment("2018-01-01"); //just one random past date I know it is for sure before any future contract creation on binance
for (let i = 0; i < markets.length; i++) {
let fundingRates = [];
symbol = markets[i];
let startTime, endTime, days_per_batch;
const initialResult = await binance.futuresFundingRate(symbol, { //make a request without a range to get a start and endpoint for pagination
limit: 1000,
});
startTime = moment(initialResult[0].fundingTime); //use the first retrieved item as startTime
endTime = moment(initialResult[initialResult.length - 1].fundingTime); //use last received item as endTime
days_per_batch = endTime.diff(startTime, "days"); //difference in days between first and last retrieved item of initial request
while (endTime.isAfter(targetDate)) {
const result = await binance.futuresFundingRate(symbol, {
startTime: startTime.unix(), //cast to timestamps
endTIme: endTime.unix(), //cast to timestamps
limit: 1000,
});
//concat retrieved result with result array that gets exported to csv
fundingRates = [].concat(
...result.map((e) => {
return {
symbol: e.symbol,
fundingRate: e.fundingRate,
fundingTime: moment(e.fundingTime),
};
})
);
//set the endTime to the previosu startTime and the startTime to startTime - days per batch (difference in days between first item and last item of result)
endTime = startTime;
startTime.subtract(days_per_batch, "days");
}
exportToCSV(symbol, fundingRates);
console.log("processing " + i + " / " + markets.length)
}
} catch (error) {
console.log(error);
}
})();

How to rename Object Keys & change Object values from string to number

Alright, guys.
I am dealing with <TimePicker/> third party library from React and want to manipulate the state so I can push this new format of code into a new object later.
when I target a value using this library, whatever time I pick, my state turns out to be a string like this - e.g if I pick 20:30, this.state.time equals "20:30".
I want to manipulate this string into an object like this:
from "20:30" to time: {hour: 20, minutes: 30} ///both hour and minutes are now numbers
My full code below:
import React from "react";
import TimePicker from "react-time-picker";
import './Clock.css'
class Clock extends React.Component {
state = {
time: '0:00',
pushTime: [],
value: ''
};
onChangeTimePickerHandler = time => {
this.setState({time});
let currentTime = this.state.time;
this.props.CallbackRenderTime(currentTime)
};
//I am using TimePicker library for React and want to manipulate the income from the chosen time
pushNewTimeHandler = () => {
let time = [...this.state.time] ;// ["1", "0", ":", "0", "0"]
time.splice(2, 1);
let timeToOneString = time.join().replace(/,/g, "");
let prepareStringForNewObject = (value, index) => {
let array = value.substring(0,index) + ',' + value.substring(index);
return array.split(',')
};
let newObj = {...prepareStringForNewObject(timeToOneString,2)};
console.log(newObj); // {0: "10", 1: "00"}
};
render() {
return (
<div>
<TimePicker
onChange={this.onChangeTimePickerHandler}
value={this.state.time}/>
<button onClick={this.pushNewTimeHandler}>Push the Array of Time</button>
<p>{this.state.time}</p>
</div>
)
}
}
export default Clock
So I have been playing around and came with this really ugly part-of-the-issue solution:
//I am using TimePicker library for React and want to manipulate the income from the chosen time
manipulateTimeHandler = () => {
//this.state.time is "10:00"
let time = [...this.state.time] ;// now it looks like this: ["1", "0", ":", "0", "0"]
time.splice(2, 1);
let timeToOneString = time.join().replace(/,/g, "");
let prepareStringForNewObject = (value, index) => {
let array = value.substring(0,index) + ',' + value.substring(index);
return array.split(',')
};
let newObj = {...prepareStringForNewObject(timeToOneString,2)};
console.log(newObj); //finally it became the object, but the object values are still
//strings and the object keys still need to be changed into "hour"
//and "minutes" {0: "10", 1: "00"}
};
So on my ugly solution pushNewTimeHandler I still need to rename the object keys to hour and minutes and also need to transform the string values for hours and minutes into numbers.
I'd split the time string by :, creating an array of strings, then map each string to a number, and destructure into hour and minutes on the left of the =. Then you can make an object of hour and minutes:
const str = '20:30';
const [hour, minutes] = str.split(':').map(Number);
const obj = { hour, minutes };
console.log(obj);

How to dynamically change the date for the date key in the items prop for react-native-calendars

I am trying to change the date in the items prop for react native calendars.
From the docs, it shows that it requires a date string as a key which then requires an array of objects to display for that particular date.
Something like this.
<Agenda
items: {{
'2019-10-12': [],
'2019-10-13': [{}, {}],
'2019-10-14': [{}],
'2019-10-15': []
}}
/>
However, I do not want to hardcode the dates. What I would like is to display the dates of the next week. This is the approach I have tried
I get the date using
new currentDate = new Date();
I pass this date into weekly items to get my weekly items object.
The below function uses two other functions called addDays and dateToString to properly render the dates.
const weeklyItems = (date) => {
const day1 = dateToString(addDays(date, 1));
const day2 = dateToString(addDays(date, 2));
const day3 = dateToString(addDays(date, 3));
const day4 = dateToString(addDays(date, 4));
const day5 = dateToString(addDays(date, 5));
const day6 = dateToString(addDays(date, 6));
const currentDate = dateToString(date);
return {
currentDate : [],
day1: [],
day2: [],
day3: [],
day4: [],
day5: [],
day6: []
}
}
addDays and dateToString are my helper functions:
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
This function converts a date object into the form 'YYYY-MM-DD' which is what is required by Agenda
function dateToString (date) {
const newString = moment(date).format('YYYY-MM-DD');
return newString.toString()
}
I have checked to see if my dates were correct and they all check out. I also have used currentDate and day6 as my objects for the minDate and maxDate props for Agenda and they work as expected.
After doing this my Agenda now looks like this:
<Agenda
minDate = {currentDate}
maxDate = {day6}
selected = {currentDate}
items: {weeklyItems(currentDate)}
renderEmptyDate={() => {return (<View/>);}}
/>
And this makes it stop working. I know that I have empty arrays in my weeklyItems but for now I am just trying to get the dates working and unfortunately they are not. Any help would be appreciated.
I`m on mobile, so i can't run this code...
Try to return your dates object like that:
return {
[currentDate] : [],
[day1]: [],
...
}
Also you can rewrite your weeklyItems functions to iterate from 0 to 6 and dynamically set properties to object which will be returned.
Than in your component:
// somewhere in your js
// const dates = weeklyItems(currentDate)
// const minDate = dateToString(date)
// const maxDate = dateToString(addDays(date, 6))
<Agenda
minDate = {minDate}
maxDate = {maxDate}
selected = {minDate}
items: {dates}
renderEmptyDate={() => {return (<View/>);}}
/>

What would be the proper way to filter by date

I have an array of objects with date strings formatted as so 2019-03-22T13:36:18.333Z
I am curious what the best course of action would be to sort these by past day, past week, past month and past year.
I was thinking of just splitting at the T then splitting again at the -
I was also considering putting the date into the Date object and figuring it out that way.
What would be the most dynamic? Most efficient? etc.
const dates = [
{created_at: '2019-03-22T13:36:18.333Z'}
{created_at: '2019-05-22T13:36:18.333Z'}
{created_at: '2019-03-23T13:36:18.333Z'}
{created_at: '2019-03-24T13:36:18.333Z'}
{created_at: '2019-01-22T13:36:18.333Z'}
]
const spliteDate = (date, splitBy) => {
if(splitBy = 'day'){
return date.split("T")[0].split("-")[1]
} else if(splitBy = 'month') {
return date.split("T")[0].split("-")[2]
} else if(splitBy = 'year') {
return date.split("T")[0].split("-")[0]
}
}
dates.filter(date => {
return splitDate(date, 'month') === Date.now().getMonth()
}
Something along these lines
It is probably the easiest to convert the strings to Date objects
For instance, you have an array of dates in string format:
const arr = ['2019-03-22T13:36:18.333Z', '2019-03-28T16:36:18.333Z', '2015-05-21T16:36:18.333Z'];
Looking at your question, if you would like to filter it by a certain date, it is definitely much shorter and efficient to do this:
const filterByDate = dateFilter => arr.filter(date => new Date(date).getDate() < dateFilter);
filterByDate(25);
// result [ '2019-03-22T13:36:18.333Z', '2019-05-21T16:36:18.333Z' ]
And, if you require multiple filters, for instance filter by date AND year,
const filterByDateAndYear = (dateFilter, yearFilter) => arr.filter(date => (new Date(date).getDate() < dateFilter) && (new Date(date).getFullYear() < yearFilter));
filterByDateAndYear(25, 2018);
//result [ '2015-05-21T16:36:18.333Z' ]

Sorting array in 5 minute interval with unique value count

I'm trying to sort an array in such a way that i get the count for unique users during an interval of 5 minutes starting at 0:00 at the start of the day.
how do i define the 5 minute interval in epoch time? (the used data will be the epochtime of the current day) and how do i get the unique usercount for that interval?
Input
[1486428994000, "user a"]
[1486429834000, "user a"]
[1486429839000, "user a"]
[1486429869000, "user b"]
Desired output
[1486428900000, 1 ]
[1486429800000, 2 ]
// Remove doublons from an array.
const uniq = array =>
array.filter((value, index) => array.indexOf(value) === index);
// Your function.
const groupCount = (data, timeWindow) =>
data
.reduce((groups, line) => {
const current = groups[groups.length - 1];
// If the line is outside of the current time window, push a new group.
if (!current || line[0] > current[0] + timeWindow) {
// Find the beginning of the corresponding time window.
const windowStart = line[0] - line[0] % timeWindow;
// Push the new group.
groups.push([windowStart, [line[1]]]);
} else {
// Else add a new user to the group.
current[1].push(line[1]);
}
return groups;
}, [])
// Once we created the groups, we remove doublons from them and count users.
.map(group => [group[0], uniq(group[1]).length]);
const data = [
[1486428994000, "user a"],
[1486429834000, "user a"],
[1486429839000, "user a"],
[1486429869000, "user b"]
];
console.log(groupCount(data, 5 * 60 * 1000));
To set a repeating timer, you can use setInterval(function(){},_timeout_)
var T = setInterval(function(){
/* code here */
}, 1e3*60*5);
1e3 = 1000 (milliseconds)
x 60 (seconds)
x 5 (minutes)
for "now" in Javascript you can use:
new Date().valueOf()
depending on which type of epoch you're using you might have to divide or multiply by 100
To get unique values, you can use .reduce() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Gotchas using client side timers:
your JS time stamp takes the machine time, so if the client's machine date/time is off, your calculations will be wrong - the best way to combat this is to send a server time stamp to the front end and cast that into a javascript date object and work with that, rather than the client's machine time.
As mentioned, the epoch time stamp varies from server software to server software, so you might have to adjust either server side or client side (usually a difference of a factor of a 100)
With some timestamp logic and some array magic, you can pull this off. Although the below solution returns the correct output, I feel like the map at the end isn't entirely necessary. If anyone feels like expanding on my solution, please feel free to.
var raw = [
[1486428994000, "user a"],
[1486429834000, "user a"],
[1486429839000, "user a"],
[1486429869000, "user b"]
];
raw.map(a=> a[0] = parseInt(a[0] / (5 * 60 * 1000)) * (5 * 60 * 1000));
raw = raw.reduce(function(a,b) {
if(!a[b[0]]) a[b[0]] = {users: [], count: 0};
if(a[b[0]].users.indexOf(b[1]) === -1) { a[b[0]].users.push(b[1]); a[b[0]].count++; }
return a;
}, {});
var ret = [];
for(var i in raw) {
ret.push([i, raw[i].count]);
}
console.log(ret);

Categories

Resources