Reputation: 3070
How to transform an array of objects to array of array of objects? This is to group the objects in the original array according to some criteria. So, objects similar wrt some criteria in the original array will be put inside a sub array, which is an element of the new array.
I have problems with initial empty array insertion, which it should not be inserted, and keeping temporary states of sub arrays and temporary vars to be used as criteria.
What am I missing here? Is there a more cluttered, maybe less imperative, more functional way to achieve this?
var transform = function (array) {
var rows = [];
var parts = [];
var lastDay = null;
var i = 0;
array.forEach(function(item) {
var currentDay = new Date(item.dt_text).getDay();
if (currentDay != lastDay) {
// not in same day row
rows.push(parts);
parts = [];
parts.push(item);
lastDay = currentDay;
i = rows.indexOf(parts);
return;
} else if (currentDay == lastDay){
parts.push(item);
return;
}
});
return rows;
},
sample data to be handles bu this function is of that form:
[
{
"dt":1442275200,
"main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
"clouds":{"all":0},
"wind":{"speed":1.18,"deg":34.0052},
"rain":{},
"sys":{"pod":"n"},
"dt_text":"2015-09-15 00:00:00"
},
{
"dt":1442275200,
"main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
"clouds":{"all":0},
"wind":{"speed":1.18,"deg":34.0052},
"rain":{},
"sys":{"pod":"n"},
"dt_text":"2015-09-15 00:00:00"
},
{
"dt":1442228400,
"main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
"clouds":{"all":0},
"wind":{"speed":1.18,"deg":34.0052},
"rain":{},
"sys":{"pod":"n"},
"dt_text":"2015-09-14 00:00:00"
}
]
Upvotes: 0
Views: 122
Reputation: 14868
Here's an approach that leverages Array.prototype.reduce
(provided the array elements are already ordered based on element.dt
as your question suggests):
function transform(array) {
var groups = [[]];
var lastItemForLastGroup = array.reduce(function (previous, current) {
// continue population of the latest group
groups[groups.length - 1].push(previous);
if (previous.dt !== current.dt) {
// add a new group
groups.push([]);
}
// return current so that it will `previous` in the next iteration
return current;
});
groups[groups.length - 1].push(lastItemForLastGroup);
return groups;
}
For someone that understands the reduce
method, it's straightforward and makes sense. Otherwise it may appear unreadable (then we would say it abuses Array.prototype.reduce
instead :) ).
Using the following as test data:
var harry = [
// group 0
{
"dt": 1442275200,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-15 00:00:00"
},
{
"dt": 1442275200,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-15 00:00:00"
},
// group 1
{
"dt": 1442228400,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-14 00:00:00"
},
{
"dt": 1442228400,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-14 00:00:00"
},
{
"dt": 1442228400,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-14 00:00:00"
},
// group 2
{
"dt": 1442181600,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-13 00:00:00"
},
{
"dt": 1442181600,
"main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01n"}],
"clouds": {"all": 0},
"wind": {"speed": 1.18, "deg": 34.0052},
"rain": {},
"sys": {"pod": "n"},
"dt_text": "2015-09-13 00:00:00"
}
];
transform(harry);
returns 3 groups, where the first group contains two items, second group contains 3 items and third group contains two items as expected.
Upvotes: 0
Reputation: 54079
Here are two ways of doing it. Don't know why you have the i
or why you bind this
as you don't use eithe. Nor did you code push the last row of items onto rows.
Version one trying to keep to your syntax.
var transform = function (array) {
var rows = []; // row array
var parts; // undefined parts array
var lastDay; // undefine last day
array.forEach(function (item) {
var currentDay = new Date(item.dt * 1000).getDay(); // get day. safari returns NaN for new Date(item.dt_txt).getDay(), where dt_txt is in format in the array of question. chrome is ok. so better use timestamp to getDay().
if (currentDay !== lastDay) { // if not the same as last
// sorry this test is wrong have removed
// if (parts === undefined) { // check if there is a parts array.
parts = []; // create it and
rows.push(parts); // push it onto the rows array.
//}
parts.push(item); // push the new item onto the parts array
lastDay = currentDay; // remeber the last day.
} else {
parts.push(item); // same day so just push it onto the parts array;
}
});
return rows;
}
Version 2 the most efficient way I can think of.
var transform = function (array) {
var rows = []; // row array
var row; // current row
var lastDay; // undefine last day
array.forEach(function (item) {
var currentDay = new Date(item.dt * 1000).getDay(); // get day.
// safari returns NaN for new Date(item.dt_txt).getDay(), where dt_txt is in format in the array of question. chrome is ok. so better use timestamp to getDay().
if (currentDay !== lastDay) { // if not the same as last
row = rows.push([item])-1; // push new array onto the rows array and get new row index.
lastDay = currentDay; // remeber the last day.
} else {
rows[row].push(item); // same day so just push it onto the parts array;
}
});
return rows;
}
Upvotes: 1
Reputation: 3060
Your logic isn't bad.
But you forgot to push your row if all your data is on the same day.
So you'd have to add a rows.push(parts)
after your array.forEach()
loop.
Also, few tips.
array.forEach().bind(this)
.parts.length
instead of parts != []
Here is a jsFiddle with your stuff fixed.
Upvotes: 0