How to use Object.keys()? - arrays

I'm usually used to play with the Object.keys() function.
But this time, I'm having trouble to read the value of the properties by their name, for each object within the following JSON :
The JSON
var myData = {
"customNotes":{
"2017/04/17":{
"concernedDate":"April, 17, 2017",
"notesList":[
{
"id":25,
"title":"Note 25 Title"
},
{
"id":51,
"title":"Note 51 Title"
}
]
},
"2017/04/14":{
"concernedDate":"April, 14, 2017",
"notesList":[
{
"id":53,
"title":"Note 53 Title"
}
]
}
}
}
The OUTPUT I Need
What I Need is the following OUTPUT :
2017/04/17
concernedDate: April, 17, 2017
Number of notes: 2
Notes list:
- id: 25
- title: Note 25 Title
- id: 51
- title: Note 51 Title
- - - - - - - -
2017/04/14
concernedDate: April, 14, 2017
Number of notes: 1
Notes list:
- id: 53
- title: Note 53 Title
My buggy JS Code
$(Object.keys(myData.customNotes)).each(function(iGroup){
//Get Key/Val of each Group of notes
var keyGroup = Object.keys(myData.customNotes)[iGroup];
var valGroup = myData.customNotes[keyGroup];
//Print Key (the date as a string)
console.log(valGroup[0]);
//Print Key Property ('concernedDate')
console.log('concernedDate: ' + valGroup[0].concernedDate);
//Print Key Property Length ('notesList')
console.log('Number of notes: ' + valGroup[0].notesList.length);
//Print List of notes
console.log('Notes list:');
//If the property exists
if(valGroup[0].notesList){
//For each item in 'notesList'
$(Object.keys(valGroup[0].notesList)).each(function(iNote){
//Get Key/Val For each note
var keyNote = Object.keys(valGroup[0].notesList)[iNote];
var valNote = valGroup[0].notesList[keyNote];
//Print the properties of each note
console.log('- id: ' + valNote.id);
console.log('- title: ' + valNote.title);
});
}
});

es6
Object.keys(myData.customNotes).reduce((prev, curr) => {
const date = curr;
const concernedDate = myData.customNotes[curr].concernedDate;
const numberNotes = myData.customNotes[curr].notesList.length;
const notesList = myData.customNotes[curr].notesList.map(note => `- id: ${note.id} \n - title: ${note.title} \n\n`);
return prev + `${date} \n ${concernedDate} \n Number of notes: ${numberNotes} \n Notes list: \n ${notesList} \n - - - - - - - - \n`;
}, ''));
es5
Object.keys(myData.customNotes).reduce(function (prev, curr) {
const date = curr;
const concernedDate = myData.customNotes[curr].concernedDate;
const numberNotes = myData.customNotes[curr].notesList.length;
const notesList = myData.customNotes[curr].notesList.map(function(note) { return ‘- id: ‘ + note.id + ‘\n’ + ‘- title: ‘ + note.title + ‘\n\n’;});
return prev + 'date' + '\n' + concernedDate + '\n Number of notes: ' + numberNotes + '\n Notes list: \n' + notesList + ' \n - - - - - - - - \n';
}, ''));

First, I want to thank #cheesenthusiast for its answer: It works like a charm, and you should definitely rely on it, wether it is in ES6 or ES5 !
In the same time, I've experimented by myself, and found another working solution, in a more "classic loopy-style" way of achieving the same result.
Another working method (more classical)
So for those of you that want to have more control at every step of the script, Here is another working way to do it :
//WORKING
console.log('---START LIST---');
//For each GROUP OF NOTES
for (var item in myData.customNotes) {
//Print GROUP OF NOTES key
console.log(item);
//Print GROUP OF NOTES properties
console.log('concernedDate: ' + myData.customNotes[item].concernedDate);
console.log('Number of notes: ' + myData.customNotes[item].notesList.length);
console.log('Notes list: ');
//For each NOTE
$(Object.keys(myData.customNotes[item].notesList)).each(function(iGroup){
//Get this Array item
var keyGroup = Object.keys(myData.customNotes[item].notesList)[iGroup];
var valGroup = myData.customNotes[item].notesList[keyGroup];
//Print NOTE properties
console.log('- id: ' + valGroup.id);
console.log('- title: ' + valGroup.title);
});
console.log('- - - - - -');
}
console.log('---END LIST---');

SORTING
After having added some elements to my Object, I need to sort it by KEY (the date : example 2017/04/17).
But it always returns the same items in the same order, regardless of what I try :
//Creating an Array of SORTED values (Nothing works :-/)
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a > b ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => Date(a).valueOf() > Date(b).valueOf() ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a.valueOf() > b.valueOf() ? a : b);
var tmpArr = Object.keys(myData.customNotes).sort((a, b) => a-b);
//Convert back the Array to an Object
var myData.customNotes = tmpArr.reduce(function(acc, cur, i) {
acc[i] = cur;
return acc;
}, {});

Related

How to grab text values from getAllByTestId?

I have a component that lists a bunch of items. In this, they're cryptocurrency assets. When I click the header labelled Name, it sorts the items in alphabetical order. I would like to test this functionality by clicking the Name button in order to fire the event, and then asserting that the values in the name column are sorted alphabetically:
it("Sorts by name on click", () => {
const sortedByName = spotData
.sort((a, b) => {
return a.name < b.name ? -1 : a.name === b.name ? 0 : 1;
})
.map((ticker) => ticker.name);
const { getByText, getAllByTestId } = renderWithProviders(
<MarketSelectorPanel marketsList={spotData} />
);
fireEvent.click(getByText("Name"));
expect(getAllByTestId("market-selector-row-name")).toEqual(
sortedByName
);
});
The above doesn't work because expect(getAllByTestId("market-selector-row-name")) grabs the entire HTML element:
● MarketSelectorPanel tables › Sorts by name on click
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Array [
- "BCH/USD",
- "BTC/USD",
- "ETH/USD",
+ <span
+ class="market-selector-row-val"
+ data-testid="market-selector-row-name"
+ >
+ BCH/USD
+ </span>,
+ <span
+ class="market-selector-row-val"
+ data-testid="market-selector-row-name"
+ >
+ BTC/USD
+ </span>,
+ <span
+ class="market-selector-row-val"
+ data-testid="market-selector-row-name"
+ >
+ ETH/USD
+ </span>
]
Please try passing the XPath of the component for testing rather than the id
Solved using getNodeText:
const columnAfterClick = getAllByTestId("market-selector-row-name").map(getNodeText); // Contains array of text strings only now
expect(columnAfterClick).toEqual(sortedByName);

Display day with hours in Angularjs

Here are date and time.
Date 2018-05-25T10:35:04.000Z
Expected Output = 3 days 12 hours
I want to display date and time same like as a give above. Currently, I am using moment.js. Is there any way to display like above?
Alternatively to Zvi's suggestion, you could use the AngularJS date filter
Say in your controller you have 2 dates:
app.controller('AppCtrl', function($scope) {
ctrl = this;
ctrl.date1 = new Date("2018-05-22T22:35:04.000Z");
ctrl.date2 = new Date("2018-05-25T10:35:04.000Z");
});
And in HTML, you'd display the difference with the date filter:
{{ctrl.date2 - ctrl.date1 | date:"dd 'days' HH 'hours'"}}
Here's a working JSFiddle example
You can use this moment-precise-range.
Here's full example:
calcDiff = (date : string) => {
let diff_in_string = '';
let original_date = moment(date);
let date_time_now = moment();
let diff_in_object: any = moment-presice.preciseDiffBetweenDates(original_date, date_time_now, true);
if (diff_in_object.days > 0) {
diff_in_string = diff_in_string + diff_in_object.days + ' ';
if (diff_in_object.days === 1) {
diff_in_string += 'Day '
} else {
diff_in_string += 'Days '
}
}
if (diff_in_object.hours > 0) {
if (diff_in_object.days > 0) {
diff_in_string += 'and ' + diff_in_object.hours + ' '
} else {
diff_in_string += diff_in_object.hours + ' '
}
if (diff_in_object.hours === 1) {
diff_in_string += 'Hour '
} else {
diff_in_string += 'Hours '
}
}
diff_in_string += 'ago'
return diff_in_string;
}
You should consider using The HumanizeDuration library (you can check this answer).
It allows you to translate in any language you want the difference between two dates the way you want.
For example :
var yourdate= moment().set({'year': 2018, 'month': 5, 'day': 22});
var today = moment();
var duration = moment.duration(today.diff(yourdate));
var humanized = humanizeDuration(duration, { units: ['d', 'h'], language: language, round: true });
You can also format it with spacers, etc.

I need to show formatted output as it shown below

angular.forEach(caseChangeRecord, function(change, key) {
if (caseChangeRecord != 'undefined') {
body += change.fieldName + ' : ' + change.newValue;
}
});
Actual Display Output:
A:BA:A:BA:B
Need to display Output:
A:B
A:B
A:B
body += change.fieldName + ' : ' + change.newValue + ' < br>';
if (caseChangeRecord != 'undefined') condition within your loop has no affect, since if caseChangeRecord is undefined, we will never enter the loop, in my example I assume that caseChangeRecord is an array (if it is an object, we can use Object.keys(caseChangeRecord) and get each item by its key).
Also, you probably need to use $sce service and its trustAsHtml() function:
var caseChangeRecord = [
{
fieldName: 'A1',
newValue: 'B1'
},
{
fieldName: 'A2',
newValue: 'B2'
},
{
fieldName: 'A3',
newValue: 'B3'
}
];
if (caseChangeRecord) {
$scope.body = $sce.trustAsHtml(caseChangeRecord.map(function (change) {
return '<p>' + change.fieldName + ' : ' + change.newValue + '</p>';
}).join(''));
}
then in your HTML you can use it like:
<body ng-controller="MainCtrl" ng-bind-html="body"></body>
it is not exactly your case, but I think this way you can solve your issue, if you need an exact solution for your case, update my plunker and I will try to help.
plunker: https://plnkr.co/edit/Mk1Fbguce7ctXlVN6qsf?p=preview

How to filter the grid data(Ng-grid) on the basis of selection date range via AngularJS

Actually I have a combo box with values "Last seven days", "Last three days" and "Today", I want to apply the filter on the basis of selected value date range by current date
If the filterOptions of the grid are not working for you then try keeping your full set of data in one array and the filtered set in another. Set the gridOptions.data to your filtered set. Apply a function when the user makes a selection from the dropdown that sets your filtered set to whatever you want. Something like this:
<select ng-model="filterValue"><option value=1>Last 7 days</option></select>
$scope.allItems = [{someDate:'10/21/14'},{someDate:'11/2/14'},{someDate:'10/24/14'}];
$scope.filteredItems = [];
$scope.filterValue;
$scope.$watch("filterValue", function() {
// filter your dataset here
if($scope.filterValue == 1) { // Last 7 days
angular.forEach(allItems,function(value) {
// Use something like moment.js to do date arithmetic
if(date in last 7 days) {
$scope.filteredItems.push(value);
}
});
}
};
$scope.gridOptions = { data: 'filteredItems' ....};
Below is the solution for filtering the data of the grid view on the basis of selected Date options from the combo box as described in the problem statement:
Here durationFilter() is the function call on the change of the value in the combo box
$scope.durationFilter = function () {
var currentDate = new Date();
var difDate = new Date();
$scope.filteredItems = [];
switch ($scope.selectedItem.id) {
case 1:
$scope.range = 1;
break;
case 2:
$scope.range = 3;
break;
case 3:
$scope.range = 7;
break;
default:
$scope.range = 0;
$scope.filteredItems = [{ 0: new Date() }];
$scope.gridOptions.filterOptions.filterText = '';
break;
}
for (var i = 0; i < $scope.range; i++) {
currentDate.subtractDays(i);
difDate = currentDate;
$scope.difDate = $filter('date')(difDate, 'MM/dd/yyyy');
$scope.filteredItems.push($scope.difDate);
currentDate = new Date();
}
$scope.searchingFilters.filteredDate = $scope.filteredItems;
$scope.setFilterText();
};
$scope.setFilterText = function () {
$scope.gridOptions.filterOptions.filterText = 'Submit Time:' + $scope.searchingFilters.filteredDate[0] + '|' + $scope.searchingFilters.filteredDate[1] + '|' +
$scope.searchingFilters.filteredDate[2] + '|' + $scope.searchingFilters.filteredDate[3] + '|' + $scope.searchingFilters.filteredDate[4] +
'|' + $scope.searchingFilters.filteredDate[5] + '|' + $scope.searchingFilters.filteredDate[6] + ';';
}

How to pass value to Angular $interval function

Condition
I have an array of HTML element constructed using ngRepeat.
<li ng-repeat="person in persons">Name: {{ person.name }}. Age {{ person.age }}</li>
In my controller I am trying to update (random) the person's "age" dynamically using Angular $interval.
$scope.persons = [
{name: "John Doe", age: 30},
{name: "Peter Parker", age: 21},
{name: "Clark Kent", age: 45},
{name: "John Wayne", age: 33}
];
var promiseOfYouth = [];
$scope.makeYoung = function() {
//since persons are store in array object.
//loop persons to update dynamically each person age
for(var x = 0; x < $scope.persons.length; x++) {
//check age randomizer already running
if ( angular.isDefined(promiseOfYouth[x]) ) return;
//make them young!
promiseOfYouth[x] = $interval(function() {
console.log('x value in $interval function: '+x);
try {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50);
}
}
Problem
In the makeYoung function the "x" value always return "4"
Question
How do I passed value of "x" (in this case it should 0,1,2,3) into the $interval function?
For further details please take a look at my jsFiddle example here
That's a classical JavaScript gotcha. You need to have x in a new scope to make this work. Otherwise, all the closures use the value of the same x variable, which is 4 at the end of the loop.
See the jsfiddle
function makePersonYoung(x) {
//check age randomizer already running
if ( angular.isDefined(promiseOfYouth[x]) ) return;
//make them young!
promiseOfYouth[x] = $interval(function() {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
}, 100, 50);
}
$scope.makeYoung = function() {
//since persons are store in array object.
//loop persons to update dynamically each person age
for(var x = 0; x < $scope.persons.length; x++) {
makePersonYoung(x);
}
}
This solution has the added advantage of making the code more readable.
This is a quirk of javascript. That blog post suggests several solutions. Copy the x variable as an immediately executing function:
(function(x2) {promiseOfYouth[x2] = $interval(function() {
console.log('x value in $interval function: '+x2);
try {
$scope.persons[x2].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x2+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50)}(x);
Or bind the the function
promiseOfYouth[x] = $interval(function(x2) {
console.log('x value in $interval function: '+x2);
try {
$scope.persons[x2].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x2+'].age" is not valid. Error message:'+err.message);
}
}.bind(null, x), 100, 50);
you can inject it like a parameter
promiseOfYouth[x] = $interval(function(x) {
console.log('x value in $interval function: '+x);
try {
$scope.persons[x].age = Math.floor(Math.random() * (99 - 17 + 1)) + 17;
} catch(err) {
console.log('"$scope.persons['+x+'].age" is not valid. Error message:'+err.message);
}
}, 100, 50);
fiddle example

Resources