Reformat list to grouped structure - javascript

I use knockout with SharePoint, I get data using getJSON query to the list of all news. I need to create a sidebar with chronological grouped list with following structure: Year -> Month -> News title.
I already set up my view and can display all data in the format that's in initialData set.
Could you please help me to reformat this list:
var obj = [{
"Year":"2018",
"Month":"January",
"Title":"News 18 January 2018"
},
{...}];
to this view:
var initialData =
[{
Year: "2018",
Months: [{
Month: "January",
News: [
{Title: "News 18 January 2018"},
{Title: "News 23 January 2018"}]
},
{Month: "February",
News: [{...}]},
]
},{ Year: "2017", Months: [{...}]
}]
I tried Object.keys with forEach loop, for loop, but totally confused.

You can't solve this by using just a single forEach loop or Object.keys. You will have to group objects on multiple keys and in multiple levels.
First, build an object with years and months as keys, to be able to find the matching arrays for each object quickly. Then expand this key-value map to a nested array.
var years = {};
obj.forEach(function(o) {
years[o.Year] = years[o.Year] || { months: {} };
years[o.Year].months[o.Month] = years[o.Year].months[o.Month] || [];
years[o.Year].months[o.Month].push(o.Title);
});
var initialData = Object.keys(years).map(function(year) {
return {
Year: year,
Months: Object.keys(years[year].months).map(function(month) {
return {
Month: month,
News: years[year].months[month].map(function(title) {
return {
Title: title,
};
})
};
})
};
});

Related

Add two variables together/find

It works except when the subject unless the subject is the same name. Then I get the first date to the second subject that is the same.
I can't change the array since it's through API. However can I make so somehow if the first date is already set on math, then it should add the second date to second subject? Now the second subject get's the first date
var subjects = [
{ name: "math" }, //The first
{ name: "sports" },
{ name: "math" }, //The second
{ name: "art" },
];
var subjectdates = [
{ name: "math", year: 2017 }, //first date
{ name: "sports", year: 2018 },
{ name: "math", year: 2019 }, //second date
{ name: "art", year: 2020 },
];
const addDates = subjects.map((classes) => ({
subject: classes,
end_subject_date: subjectdates.find((item) => classes.name == item.name),
}));
console.log(addDates);
Using Array#reduce on subjectdates, construct a Map where the key is the name and the value is a list of the elements of this name.
Then, in the loop, to get the end_subject_date, you can use Map#get to get the list of elements of this name, and Array#shift to get and remove the first element:
const
subjects = [ {name:"math"}, {name:"sports"}, {name:"math"}, {name:"art"} ],
subjectdates = [ {name:"math",year:2017}, {name:"sports",year:2018}, {name:"math",year:2019}, {name:"art",year:2020} ];
const subjectDatesMap = subjectdates.reduce((map, item) =>
map.set(
item.name,
[...(map.get(item.name) || []), item]
)
, new Map);
const addDates = subjects.map(classes => ({
subject: classes,
end_subject_date: (subjectDatesMap.get(classes.name) || []).shift()
}));
console.log(addDates);
If you have the same keys in arrays:
Sort array by keys:
subjects = subjects.sort((a,b)=>a.name>b.name?1:-1);
subjectdates = subjectdates.sort((a,b)=>a.name>b.name?1:-1);
Insert values by index:
const result = subjects.map((s, i)=>
({ subject:s.name, end_subject_date:subjectdates[i].year}) );

How to omit values while using groupby

I have an array of JSON objects like the following:
{ BlockId: '979',
Day: 'Preliminary/Final Qualifying Day 1',
Event: 'Preliminary Qualifying',
FirstName: 'some',
LastName: 'one',
PlayerPosition: '6',
TimeSlot: '4/21/2018 12:00:00 PM'
}
I wanted to group the objects based on values and did the following:
var result = _(json)
.groupBy('Day')
.mapValues(function(groupedByDay) {
return _(groupedByDay)
.groupBy('Event')
.mapValues(function (groupedByDayAndEvent) {
return _.groupBy(groupedByDayAndEvent, 'BlockId');
})
.value();
})
.value();
Which gave me the following:
{"Preliminary/Final Qualifying Day 1":
{"Preliminary Qualifying":
{ "977":[{"BlockId":"977",
"Day":"Preliminary/Final Qualifying Day 1",
"Event":"Preliminary Qualifying",
"FirstName":"some",
"LastName":"one",
"PlayerPosition":"0",
"TimeSlot":"4/21/2018 9:00:00 AM"
}]
}
}
}
I'd like to remove the fields that I grouped by. Namely: BlockId, Day and Event. Any ideas on how I could omit these values with the code I presented? I'm lost :(
EDIT:
It seems that I forgot that _.omit creates a copy of the object without the fields given... I came to this solution that I don't believe is efficient at all. Any suggestions to make it better?
for(var day in result) {
for(var event in result[day]) {
for(var block in result[day][event]) {
for(var item in result[day][event][block]) {
delete result[day][event][block][item].BlockId;
delete result[day][event][block][item].Day;
delete result[day][event][block][item].Event;
}
}
}
}
use _omit in the deepest level
let myArr = [{
"BlockId": "979",
"Day": "Preliminary/Final Qualifying Day 1",
"Event": "Preliminary Qualifying",
"FirstName": "Robert",
"LastName": "Oristaglio",
"PlayerPosition": "6",
"TimeSlot": "4/21/2018 12:00:00 PM"
}]
var result = _(myArr)
.groupBy('Day')
.mapValues(function(groupedByDay) {
return _(groupedByDay)
.groupBy('Event')
.mapValues(function(groupedByDayAndEvent) {
return _(groupedByDayAndEvent)
.groupBy('BlockId')
.mapValues(function(groupedByAll) {
return groupedByAll.map(function(entry) {
return _.omit(entry, ['Day', 'Event', 'BlockId']);
})
})
.value();
})
.value();
})
.value();
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

sort array of objects by property in object

I have an array of objects like:
[
{date: "2016-01-07T15:01:51+00:00", text: "Lorem ipsum"},
{date: "2016-22-08T15:04:36+00:00", text: "dolor"},
// etc.
]
How's the best way to sort these by the date property? I'm already mapping this array to a react component, so any solution that works within the map function I guess would be preferred, but not essential.
I'm trying to use the sort() method at the moment, but can't work how to feed it the date property.
You can have a custom sort function:
var data = [{
date: "2016-07-01T15:01:51+00:00",
text: "Lorem ipsum"
}, {
date: "2016-02-22T15:04:36+00:00",
text: "dolor"
}, {
date: "2015-08-22T15:04:36+00:00",
text: "test"
}]
var result = data.sort(function(a, b) {
var date1 = new Date(a.date);
var date2 = new Date(b.date);
console.log(date1, date2);
return (+date1 - +date2);
});
console.log(result)

OrderBy in Angular.js for (key,value) object in reverse based on keys

I want to display the following results in the reverse order using javascript / Angular.js. This is an associative array with keys as Year(Numerical) and value as array of objects of months of those year.
var obj = { '1991': [{'year':1991,'month':'Jan'},{'year':1991,'month':'Feb'}],
'1992': [{'year':1992,'month':'March'},{'year':1992,'month':'Dec'}],
'2000': [{'year':2000,'month':'Jan'}]
}
SO when this basically shows data on html using ng-repeat="(key,value) in obj" it shows 1991 as first and 2000 as last. I just want to reverse my order of display showing 2000 as first and 1991 as last, using minimalist Complexity on Front-end i.e O(n). Applying orderBy is not helping me. I am not sure if i need a custom filter for this. Any leads would be appreciated as I dont want to change the JSON structure. It should be the same.
cheers.
Get the objects keys and then use the keys to get the value from the object.
HTML
<div ng-repeat="key in keys">
{{obj[key]}}
</div>
CONTROLLER
$scope.obj = {
'1991': [{
'year': 1991,
'month': 'Jan'
}, {
'year': 1991,
'month': 'Feb'
}],
'1992': [{
'year': 1992,
'month': 'March'
}, {
'year': 1992,
'month': 'Dec'
}],
'2000': [{
'year': 2000,
'month': 'Jan'
}]
};
// get object keys and sort them.
$scope.keys = Object.keys($scope.obj).reverse();
JSFIDDLE

Return individual days opening hours from foursquare venue endpoint

The foursquare api returns the following, located at data.response.venue.hours, for the venue opening hours at:
hours: {
status: "Closed until Noon",
isOpen: false
timeframes: [
{
days: "Mon–Wed",
open: [
{
renderedTime: "Noon–11:00 PM"
}
],
segments: [ ]
},
{
days: "Thu",
includesToday: true,
open: [
{
renderedTime: "Noon–Midnight"
}
]
segments: [ ]
},
{
days: "Fri–Sat",
open: [
{
renderedTime: "11:00 AM–1:00 AM"
}
]
segments: [ ]
},
{
days: "Sun",
open: [
{
renderedTime: "Noon–10:30 PM"
}
]
segments: [ ]
},
]
}
The group of days varies from venue to venue, i.e. some might have Mon-Tue, Wed-Sat, Sun or another variation instead of the above.
I'm looking to sort this information so that I can return the opening hours for individual days, i.e. call Monday on it's own. My javascript knowledge isn't that great so where to start would be good.
Thanks for any help.
For getting venue hours, you can use the venues/VENUE_ID/hours endpoint, which has a more machine-friendly result
hours: {
timeframes: [
{
days: [
1
2
3
4
5
6
7
],
includesToday: true,
open: [
{
start: "0600",
end: "2000"
}
],
segments: [ ]
}
]
}
This is a simplified case where all 7 days have the same open segment, but in other cases you should be able to iterate through timeframes and get each day's open array.
var Mon;
if (timeframes[j].days.indexOf(i) != -1) {
Mon = timeframes[j].open;
}
I would start with a mapping object describing what days match with the particular opening times:
var diff = {
mon: 'Mon–Wed',
tue: 'Mon–Wed',
wed: 'Mon–Wed',
thu: 'Thu',
fri: 'Fri-Sat',
sat: 'Fri-Sat'
};
Then, instead of sorting the data I would use filter to pull out the relevant result from the data using the mapping object:
function getOpeningTimes(day) {
var arr = hours.timeframes.filter(function (el) {
// return the match where the value of the property
// in the mapping object that matches the search equals the
// days property in the current object in the data.
return diff[day] === el.days;
});
return arr[0].open[0].renderedTime;
}
console.log(getOpeningTimes('mon')); // Noon–11:00 PM
Fiddle

Categories

Resources