Create ranges of dates from a single array of dates - javascript

How can I make ranges of dates (from the newest to the oldest) from one array of dates?
I have this array of dates:
["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"]
And I want to have the following ranges of dates result:
["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z"]
["2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z"]
["2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"]
I want to achieve this in JavaScript

Assuming that the dates are ordered, you can iterate with for loop up to the last item, and Array#slice each pair:
var dates = ["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"];
var ranges = [];
for(var i = 0; i < dates.length - 1; i++) {
ranges.push(dates.slice(i, i + 2));
}
console.log(ranges);

You could take the actual element of the array and the next for a new item of the result with a Array#reduce pattern which returns the next item for the next loop as actual item.
var data = ["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"],
result = [];
data.sort((a, b) => b > a || -(b < a)); // desc
data.reduce((a, b) => (result.push([a, b]), b));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

var data = ["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"];
var paired = data.sort().slice(0, -1).map(function(d, i) {
return [d, data[i+1]];
})
console.log(paired);
Or using es6 you could do it in one line:
let data = ["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"];
let paired = data.sort().slice(0, -1).map((d, i) => [d, data[i+1]])
console.log(paired)

Since you don't need any advanced sorting, you can just use the default .sort() function which javascript provides.
var datesSorted = dates.sort();
Splitting into pairs can be done in a cycle, because JavaScript doesn't provide any direct "split array into pairs" function.
The final script should look something like this:
var dates = ["2017-11-16T12:12:50.323Z", "2017-11-15T16:13:24.219Z", "2017-11-14T16:13:55.449Z", "2017-11-13T06:56:21.332Z"];
var datesSorted = dates.sort();
var datePairs = function(datesSorted) {
var pairs = [];
for (var i=0 ; i<datesSorted.length ; i+=2) { //iterate by two
if (datesSorted[i+1] !== undefined) { //check if a date has a "pair"
pairs.push ([datesSorted[i], datesSorted[i+1]]); //if it does, push them both
} else {
pairs.push ([datesSorted[i]]);
}
}
return pairs;
};
You're array of date arrays should now be stored in datePairs.

Related

Split sorted array of ISO 8601 dates into new 2d arrays for each day in Javascript

I'm trying to split a sorted array containing ISO 8601 (UTC) dates into a 2D array, where each day represents an array containing all of the dates where the year/month/day match.
Example array:
let dates = [
'2019-07-14T08:02:27Z',
'2019-07-05T17:22:34Z',
'2019-06-25T21:23:30Z',
'2019-06-25T16:46:55Z',
'2019-06-18T14:14:15Z',
'2019-06-18T13:09:07Z'
];
Ideally, the above array would create the following 2D array:
let dates = [
['2019-07-14T08:02:27Z'],
['2019-07-05T17:22:34Z'],
['2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z'],
['2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z']
];
I've tried quite a few approaches but for the sake of providing some code, I substring each array element date[i].substring(0, 10) which returns the year/month/day, say 2019-07-14 - since the array is sorted I compare the previous and current element and decide if the element should be added to a new array or not.
let arr = [], temp = [];
for (let i = 0, prev = null; i < dates.length; i++) {
temp.push(dates[i]);
if (!(dates[i].substring(0, 10) === prev)) {
arr.push(temp);
temp = [];
}
prev = dates[i].substring(0, 10);
}
Code doesn't work as it is off by one in some cases, e.g:
[ '2019-07-14T08:02:27Z' ],
[ '2019-07-10T14:22:04Z' ],
[ '2019-07-09T16:08:22Z' ], // should be in below array
[
'2019-07-09T15:58:55Z',
'2019-07-09T14:41:49Z',
'2019-07-09T14:12:04Z',
'2019-07-09T14:10:29Z',
'2019-07-09T13:34:46Z',
'2019-07-09T13:28:14Z',
'2019-07-08T15:51:38Z' // conflicting dates
],
[ '2019-07-08T15:45:07Z', '2019-07-07T15:07:47Z' ], // conflicting dates
Any suggestions?
You could reduce the array by checking the date substring.
let dates = ['2019-07-14T08:02:27Z', '2019-07-05T17:22:34Z', '2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z', '2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z'],
result = dates.reduce((r, s, i) => {
if (!i || r[r.length - 1][0].slice(0, 10) !== s.slice(0, 10)) r.push([]);
r[r.length - 1].push(s);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Simply maintain a map of all the dates, and keep pushing the dates to the array if they are present in the map, Object.values() on the map will give you the desired result, This works fine even when the dates are not sorted:
let dates = [ '2019-07-14T08:02:27Z', '2019-07-05T17:22:34Z', '2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z', '2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z' ];
let resullt = Object.values(dates.reduce((acc,d) => {
let date = d.substr(0,10);
(acc[date] = acc[date] || []).push(d);
return acc;
},{}));
console.log(resullt);
let arr = [], temp = [];
for (let i = 0, prev = null; i < dates.length; i++) {
if (
prev &&
!(dates[i].substring(0, 10) === prev)
) {
arr.push(temp);
temp = [];
}
temp.push(dates[i]);
prev = dates[i].substring(0, 10);
}
arr.push(temp);
You could use a function generator to group the dates according to a desired criteria.
This likely is going to be less compact than other solutions, but should be easier to maintain and to read.
Below example acquires two dates at a time and checks whether the current one (first) satisfy the provided criteria against the next one. If it does, it accumulates it and continues, otherwise it yield the previous block and continues the cycle until finished.
Below is the implementation:
let dates = [
'2019-07-14T08:02:27Z',
'2019-07-05T17:22:34Z',
'2019-06-25T21:23:30Z',
'2019-06-25T16:46:55Z',
'2019-06-18T14:14:15Z',
'2019-06-18T13:09:07Z'
];
function* groupSimilarDates(dateArr, criteria) {
var acc = [];
for (var i = 0; i < dateArr.length; i++) {
var [currDate, nextDate] = [dateArr[i],dateArr[i+1]];
if (nextDate) {
if (criteria(currDate, nextDate)) acc.push(currDate);
else {
if (acc.length) {
acc.push(currDate);
yield acc, acc = [];
}
else yield [currDate];
}
}
else {
if (criteria(currDate, dateArr[i-1])) acc.push(currDate), yield acc;
else {
if (acc.length) yield [acc];
yield [currDate];
}
}
}
}
console.log([...groupSimilarDates(dates, (a,b) => a.substring(0,10) === b.substring(0,10))]);

Arrange array like gaussian function (max values in middle, min values in edges)

How can I arrange an array like a gaussian function, meaning max values in the middle, min values in edges?
e.g.
var Array = [5,2,7,4,1]
will output the following array:
[1,4,7,5,2]
I didn't used underscore functions but you can use equivalent function from underscore/lodash to shorten code.
Steps:
Sort the array in descending order
Iterate over array and add the elements from sorted array at the start and end alternately
var arr = [5, 2, 7, 4, 1];
var sortedArr = arr.sort(function(a, b) {
return b - a;
});
var gaussianArr = [];
sortedArr.forEach(function(e, i) {
if (i % 2) {
gaussianArr.push(e);
} else {
gaussianArr.unshift(e);
}
});
console.log(gaussianArr);
document.write(gaussianArr);
Want underscore solution?
Here you go. fiddle. You won't see much difference between Vanilla JS solution and underscore solution(as the logic is same, only different syntax).
Here is the logic.
function gSort(arr) {
var _a = arr.slice()
_a.sort(function(a,b){return a-b});
_a.reverse();
var _isstart = false;
var _out = [];
for (var i = 0; i < _a.length; i++) {
if (i%2) {
_out.push(_a[i])
}else{
_out.splice(0,0,_a[i]); //You can use _out.unshift(_a[i]); also
}
}
return _out;
}
var array = [5,2,7,4,1]
console.log(gSort(array));

Sort Array in special manner

I have a problem sorting an array. I am not the smartest concerning these sort algorithms.
The array should have following structure:
var arr = [
[week, IssuesPriority1, IssuesPriority2, IssuesPriority3],
[week, IssuesPriority1, IssuesPriority2, IssuesPriority3],
[week, IssuesPriority1, IssuesPriority2, IssuesPriority3],
...
];
So for each week there is a number of issues for the priority very high, high, medium.
The string that needs to be parsed in this structure is following:
var string =
"26|3|1,27|6|1,28|7|1,29|2|1,30|2|1,31|2|1,32|2|1,33|3|1,
35|1|1,34|2|1,36|0|1,37|0|1,38|1|1,26|11|2,27|10|2,28|9|2,
29|13|2,30|10|2,31|8|2,32|10|2,33|12|2,34|14|2,35|11|2,
36|11|2,37|12|2,38|14|2,27|17|3,26|13|3,29|26|3,28|21|3,30|25|3,
31|20|3,34|30|3,32|18|3,33|25|3,35|33|3,36|28|3,38|28|3,37|27|3";
var arr = string.split(",");
for(var i = 0; i < arr.length; i++){
var currentArr = arr[i].split("|");
var week = currentArr[0];
var issues = currentArr[1];
var priority = currentArr[2];
}
I have a lack of ideas sorting it in the desired way. Can you help me?
I don't think you want any sorting at all. You are looking for grouping!
var arr = string.split(",");
var weeks = {};
for (var i = 0; i < arr.length; i++) {
var currentArr = arr[i].split("|");
var week = currentArr[0];
var issue = currentArr[1];
var priority = currentArr[2];
if (!(week in weeks))
weeks[week] = {1:[], 2:[], 3:[]};
// if the number of issues levels were unknown,
// you'd start with an empty object instead
// and create the arrays dynamically in a manner similar to the weeks
weeks[week][priority].push(issue);
}
return Object.keys(weeks).map(function(week) {
return [week, weeks[week][1], weeks[week][2], weeks[week][3]];
});
(to get the result ordered by week number, add a sort(function(a,b){return a-b}) before the .map() call)
In your situation I would recommand to put the values in the array first. In the second step I would sort the array using the sort method.
function getSortedArrayByString(myString) {
var arraySplittedString, i, tmpValueArray, tmpInnerArray, resultingArray;
arraySplittedString = myString.split(",");
resultingArray = [];
for(i = 0; i < arraySplittedString.length; i++){
// tmpArray has the format of [<week>, <IssuesPriority1>, <IssuesPriority2>]
tmpValueArray = arraySplittedString[i].split("|");
// Push it in the new array.
resultingArray.push(tmpValueArray);
}
// Sort array by weeks ascending.
resultingArray.sort( function (a, b) {
return a[0] - b[0];
});
return resultingArray;
}
Running fiddle.
If you also want to sort by the count of issues, you simply can customize the inner sort function.
With this solution all values are saved as strings. You can convert them by using the parseInt function.

How to dynamically slice Array in Javascript

I have a month array in javascript, for example:
2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,
2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,2013/04/01,
2012/09/01,2012/10/01,2012/11/01,2012/12/01
What I wanna separate the Array is that:
if (monthArray[i] > monthArray[i + 1])
// slice the Array.
So, for the above example, I should get 3 new Arrays. They are:
Array1: 2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01
Array2: 2012/09/01,2012/10/01,2012/11/01,2012/12/01,2013/01/01,2013/02/01,2013/03/01,2013/04/01
Array3:2012/09/01,2012/10/01,2012/11/01,2012/12/01
I know it is easy to do it if we know specific length, my question is, how to do it if we dynamically get a month Array(it may be divided into n groups). How to do that? Thanks!
I don't know of any better way than to iterate over the array to build your slices:
var arr = ['2012/09/01','2012/10/01','2012/11/01','2012/12/01','2013/01/01','2013/02/01','2013/03/01','2012/09/01','2012/10/01','2012/11/01','2012/12/01','2013/01/01','2013/02/01','2013/03/01','2013/04/01','2012/09/01','2012/10/01','2012/11/01','2012/12/01'];
var slices = [];
var start = 0;
for (var i=0; i<arr.length; i++) {
if (check(arr, i)) {
slices.push(arr.slice(start, i+1));
start = i+1;
}
}
function check(array, index) {
if (index+1 === array.length) return true;
return Date.parse(array[index]) > Date.parse(array[index+1]);
}
This solution has the advantage that it doesn't build the slices one element at a time, instead it builds them one slice at a time.
Assuming you want an array-of-arrays as a result, you can do this with .reduce:
var partitions = dateList.reduce(function(rv, month) {
var partition = rv[rv.length - 1], prevMonth = partition[partition.length - 1];
if (!prevMonth || prevMonth < month)
partition.push(month);
else
rv.push([month]);
return rv;
}, [ [] ]);
Starting from a list of partitions with one (empty) partition, this just checks the last month in the last partition to see if it's smaller than the current month under examination. If so (or if we're on the very first one), we add the month onto the partition. If not, then a new partition is started, containing just the current month.
So assuming you want to end up with an array of arrays, then just do it with a for loop...
var result = []; //this will contain multiple arrays once finished
var currentArray = [];
for (var i = 0; i < monthArray.length; i++) {
currentArray.push(monthArray[i]);
if (i < monthArray.length - 1 && monthArray[i] > monthArray[i + 1]) {
result.push(currentArray);
currentArray = [];
}
}
result.push(currentArray);
//result[0] is Array1
//result[1] is Array2
//result[2] is Array3
Here is a working example

Sorting an array by another index array

I'm iterating over array couples and I need to sort one by the order of the other.
Say I have these two arrays:
aLinks = [4,5,6]
bLinks = [1,2,3,4,5,6]
I need to return:
aLinks = [4,5,6]
bLinks = [4,5,6,1,2,3]
meaning that i need to have the items that match first array first and than the rest,
sorted by order if possible.
I'm working with d3 so I'm using forEach to go through the link sets and save the order of aLinks.
I don't know how to apply this order to bLinks
var linkOrder = [];
linkSets.forEach(function(set, i) {
linkOrder = [];
set.aLinks.forEach(function(link,i){
linkOrder.push(link.path);
})
});
You can do it like:
Take out the matching items from second array into a temp array
Sort the temp array
Sort the second array containing only items that did not match
Concatenate the second array into the temp array
Code - With the fix provided by User: basilikum
var first = [4,5,6];
var second = [1,7,3,4,6,5,6];
var temp = [], i = 0, p = -1;
// numerical comparator
function compare(a, b) { return a - b; }
// take out matching items from second array into a temp array
for(i=0; i<first.length; i++) {
while ((p = second.indexOf(first[i])) !== -1) {
temp.push(first[i]);
second.splice(p, 1);
}
}
// sort both arrays
temp.sort(compare);
second.sort(compare);
// concat
temp = temp.concat(second);
console.log(temp);
Working Demo: http://jsfiddle.net/kHhFQ/
You end up with A + sort(A-B) - so you just need to compute the difference between the 2 arrays. Using some underscore convenience methods for example:
var A = [4,5,6];
var B = [1,2,3,4,5,6];
var diff = _.difference(A,B);
var result = _.flattern(A, diff.sort());
iterate the first array, removing the values from the second array and then appending them to the start of the array to get the right order :
var arr1 = [4,5,6];
var arr2 = [1,2,3,4,6,5];
arr1.sort(function(a,b) {return a-b;});
for (i=arr1.length; i--;) {
arr2.splice(arr2.indexOf(arr1[i]), 1);
arr2.unshift( arr1[i] );
}
FIDDLE

Categories

Resources