Array sort based on string value not working - javascript

I am trying to sort an Array using a String field and it is sorting it wrongly.
My code looks like this.
let tempWEArray = [
{
"from" : "09/2005",
"to" : "11/2006"
},
{
"from" : "09/2006",
"to" : "11/2007"
},
{
"from" : "12/2007",
"to" : "01/2009"
},
{
"from" : "01/2009",
"to" : "12/2012"
},
{
"from" : "01/2013",
"to" : "03/2018"
}]
function sortBy(prop){
return function(a,b){
if( a[prop] < b[prop])
{
return -1;
}
else if( a[prop] > b[prop] )
{
return 1;
}
return 0;
}
}
console.log(tempWEArray.sort(sortBy("to")))
The output obtained is like below.
0: Object { from: "12/2007", to: "01/2009" }
​
1: Object { from: "01/2013", to: "03/2018" }
​
2: Object { from: "09/2005", to: "11/2006" }
​
3: Object { from: "09/2006", to: "11/2007" }
​
4: Object { from: "01/2009", to: "12/2012" }
The Array isn't getting sorted properly as you can see above. One field is misplaced. Am i doing something wrong?
All the below answers work, I've selected the Answer which I have implemented. Thanks everyone.

You could first parse those dates and then you can use - to sort them.
let arr = [{"from":"09/2005","to":"11/2006"},{"from":"09/2006","to":"11/2007"},{"from":"12/2007","to":"01/2009"},{"from":"01/2009","to":"12/2012"},{"from":"01/2013","to":"03/2018"}]
const parse = str => {
let date = new Date;
let [month, year] = str.split('/')
date.setYear(year);
date.setMonth(+month - 1)
return date;
}
const sortBy = prop => (a, b) => {
return parse(b[prop]) - parse(a[prop])
}
arr.sort(sortBy('to'))
console.log(arr)

Convert it to a date in your sort and it'll work as you intended.
seperate each component of the date string and reverse it
const dateArray = b[prop].split("/").reverse()
Use the spread operator to create a timestamp with Date.UTC and then use new Date to create a date.
new Date(Date.UTC(...dateArray))
Then use the - between two dates to find which one is bigger in a sort function.
Some example:
const res = new Date(Date.UTC(..."11/2006".split("/").reverse()))
console.log(res);
Full solution:
let tempWEArray = [{"from":"09/2005","to":"11/2006"},{"from":"09/2006","to":"11/2007"},{"from":"12/2007","to": "01/2009"},{"from":"01/2009","to": "12/2012"},{"from":"01/2013","to": "03/2018"}]
function sortBy(prop) {
return function(a, b) {
const dateArr1 = a[prop].split("/").reverse();
const dateArr2 = b[prop].split("/").reverse();
//make sure months are between 0 and 11
//can be skipped if this is already ensured.
dateArr1[1]--;
dateArr2[1]--;
return new Date(Date.UTC(...dateArr2)) - new Date(Date.UTC(...dateArr1));
}
}
console.log(tempWEArray.sort(sortBy("to")))

You can use moment.js for date camparation. example:
let tempWEArray = [
{
"from" : "09/2005",
"to" : "11/2006"
},
{
"from" : "09/2006",
"to" : "11/2007"
},
{
"from" : "12/2007",
"to" : "01/2009"
},
{
"from" : "01/2009",
"to" : "12/2012"
},
{
"from" : "01/2013",
"to" : "03/2018"
}];
const sortedArray = tempWEArray.sort(
(first, second) => moment(first.to, 'MM/YYYY')
.isSameOrAfter(moment(second.to, 'MM/YYYY')));
console.log(sortedArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment.min.js"></script>

function sortBy(prop) {
return function(a, b) {
const dateArray = b[prop].split("/").reverse()
console.log()
}
}

You could replace the date pattern with a comparable string and omit if the string has not a date pattern.
function sortBy(prop){
return function(a,b){
var valueA = a[prop].replace(/^(\d{2})\/(\d{4})$/, '$2-$1'),
valueB = b[prop].replace(/^(\d{2})\/(\d{4})$/, '$2-$1');
return valueA.localeCompare(valueB);
}
}
var tempWEArray = [{ from: "09/2005", to: "11/2006" }, { from: "09/2006", to: "11/2007" }, { from: "12/2007", to: "01/2009" }, { from: "01/2009", to: "12/2012" }, { from: "01/2013", to: "03/2018" }]
console.log(tempWEArray.sort(sortBy("to")))
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Javascript Object key sort using moment.js

I have the following object structure
{ "Apr-18" : { ... },
"Jan-18" : { ... },
"Feb-18" : { ... },
...
}
I am trying to sort the month (MMM-YY) keys so that it shows as follows
{ "Jan-18" : { ... },
"Feb-18" : { ... },
"Apr-18" : { ... },
...
}
My code for this is below. I am using moment.js to convert the date into its epoch for the sort comparison. I have roughly followed the solution shown here Sort JavaScript object by key However it's not working.
The console.log returns the object as it was, no sorting has occurred. What am I missing?
const object = {
"Apr-18" : { "a":"b" },
"Jan-18" : { "c":"d" },
"Feb-18" : { "e":"f" }
}
const sortObjectMonths = (obj) => Object.fromEntries(Object.entries(obj).sort( (a, b) =>
Date.parse(moment(a, "MMM-YY") - Date.parse(moment(b, "MMM-YY")))
));
let sorted = sortObjectMonths(object)
console.log(sorted)
You can use Object.entries() to get the object property keys and values, then use Array.sort() to sort them using moment. We can simply subtract the moment values to sort them.
The Array.sort() accepts two arguments, firstEl, secondEl, in this case that will be [key1, value1], [key2, value2]. We can use destructuring to write these as ([a,],[b,]), where a and b are the object keys (e.g. 'Apr-18').
Then we'll use Object.fromEntries() to get our sorted object.
const object = {
"Apr-18" : { "a":"b" },
"Jan-18" : { "c":"d" },
"Feb-18" : { "e":"f" },
}
console.log('Original object:', object)
const sortedObject = Object.fromEntries(
Object.entries(object).sort(([a,],[b,]) => {
return moment(a, "MMM-YY") - moment(b, "MMM-YY");
})
)
console.log('Sorted object:', sortedObject)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" referrerpolicy="no-referrer"></script>
Without moment
const months = ["Jan","Feb","Mar","Apr"]
const object = {
"Apr-18" : { "a":"b" },
"Jan-18" : { "c":"d" },
"Feb-18" : { "e":"f" },
}
const sortedObject = Object.fromEntries(
Object.entries(object)
.sort(([a,],[b,]) => months.indexOf(a.split("-")[0]) - months.indexOf(b.split("-")[0]))
)
console.log('Sorted object:', sortedObject)
Your code is almost okay but in .sort() the element a and b both are arrays of key and value. Key is at index 0 and value at index 1. Date.parse() won't work and converting the value by using new Date() is suggested. So, your code should be -
const moment = require("moment");
const sort = {
clientname: {
"Feb-18": { test: "c" },
"Jan-18": { test: "a" },
"Apr-18": { test: "v" },
},
};
const sortObjectMonths = (obj) => {
return Object.fromEntries(
Object.entries(obj).sort(
(a, b) => moment(new Date(a[0])) - moment(new Date(b[0]))
)
);
};
let sorted = sortObjectMonths(sort.clientname);
console.log(sorted);

Filter only the latest data for the same ID in an object, using timestamp in Angular 6

I have an array of objects stored in 'component' variable
component=[{id:1,date:'20-10-2020'},{id:1,date:'13-01-2020'},{id:2,date:'30-03-2020'}]
Here I'm having 2 objects with 'id' as same(id:1) but with different dates. If there are multiple objects with the same id in it, I need to take out only the id with the latest date. Is it possible with filters?
After filtering, I need the output like
component=[{id:1,date:'20-10-2020'},{id:2,date:'30-03-2020'}]
Here '{id:1,date:'13-01-2020'}' is removed from the array.
Thanks in advance
You may do so using the following code which makes use of a Map:
let map = new Map();
component.forEach(e => {
if (map.get(e.id) !== undefined) {
// current date
const [newDay, newMonth, newYear] = e.date.split("-")
let newDate = new Date(newYear, newMonth - 1, newDay);
// previous date
const [oldDay, oldMonth, oldYear] = map.get(e.id).split("-")
let oldDate = new Date(oldYear, oldMonth - 1, oldDay);
// compare the date and pick the latest
map.set(e.id, newDate > oldDate ? e.date : map.get(e.id));
} else {
map.set(e.id, e.date);
}
});
// clear the original contents
component = [];
// now populate it from the map
for (let e of map.entries()) {
component.push({id: e[0], date: e[1]});
}
console.log(component);
Try the following example please
I must move soon, if I am well I will comment on what I did
const data = [
{ id: 1, date: "20-10-2020" },
{ id: 1, date: "13-01-2020" },
{ id: 2, date: "30-03-2020" },
];
const output = data.reduce((previousValue, currentValue) => {
const entity = previousValue.find((entry) => entry.id === currentValue.id);
if (entity !== undefined) {
const [
currentValueDate,
currentValueMonth,
currentValueYear,
] = currentValue.date.split("-");
const [entityDate, entityMonth, entityYear] = entity.date.split("-");
const currentValueTimestamp = new Date(
currentValueYear,
currentValueMonth - 1,
currentValueDate
).getTime();
const entityTimestamp = new Date(
entityYear,
entityMonth - 1,
entityDate
).getTime();
const index = previousValue.findIndex(
(entry) => entry.id === currentValue.id
);
if (entityTimestamp > currentValueTimestamp) {
previousValue[index] = entity;
return previousValue;
}
}
return [...previousValue, currentValue];
}, []);
console.log(output);
See
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
https://stackoverflow.com/a/7151607/615274 Help me solve invalid date problem
/**
* Notice that I changed your date input format,
* because they are invalid as far as javascript Date object is concerned,
* or you can use momentJS.
*
* If you decide to use moment, then you could just do something like this
*
* moment('your date string goes here').format('DD-MM-YYYY')
*
* More information on date manipulation with moment can be found on their
* official website.
*
* */
let component=[{id:1,date:'10-20-2020'},{id:1,date:'01-13-2020'},{id:2,date:'03-30-2020'}]
function deDup(listOfObjects: Array<{id: number, [propName: string]: any}>): Array<{id: number, [propName: string]: any}> {
let res: Array<any> = [],
groupings: {[propName: number]: Array<any>} = {}; /**propName here will be the attribute of unique identifier, eg id */
/**First group the similar objects under the same id */
listOfObjects.forEach(object => {
if (!groupings[object.id]) groupings[object.id] = [];
groupings[object.id].push(object)
})
/**Next order the duplicates based on the date attribute */
for (let key in groupings) {
let groupingItems = groupings[key].sort((a: any, b: any) => {
return new Date(a.date) > new Date(b.date) ? 1 : 0
});
/**Now, from each grouping, pick the first one, as it is the latest, based on the sorting */
res.push(groupingItems[0]);
}
return res;
}
let res = deDup(component);
console.log('res', res)
const component = [{
id: 1,
date: '20-10-2020'
}, {
id: 1,
date: '13-01-2020'
}, {
id: 2,
date: '30-03-2020'
}];
const byLatestDate = input => {
const parse = date => {
const [day, month, year] = date.split("-");
return new Date(year, month, day);
};
return component.filter(({id,date}) =>id === input.id && date !== input.date)
.every(({date}) => parse(date) < parse(input.date));
}
console.log(component.filter(byLatestDate));
Hope it works for you.

functional way to set value in nested array to null

I want to go through both objects below and if there is no assignment with the selected datetime between the start_timestamp and the end_timestamp the values table.translateY, table.translateX and masterId in the layouts object should be set to null.
How can I achieve that?
My try:
layouts = layouts.forEach(function(layout){
assignments.forEach(function(assignment){
layout.tables.map(function(table){
if (table.id !== assignment.tableId && assignment.start_timestamp >= timestamp && !assignment.end_timestamp < timestamp) {
table.translateY = null;
table.translateX = null;
table.masterId = null;
}
});
})
});
My layouts object looks like that:
{
"id":31,
"stationId":31,
"tables":[
{
"id":652,
"number":"040",
"x":1285,
"y":527,
"length":98,
"breadth":69,
"rotation":0,
"shape":"rectangle",
"translateX":0,
"translateY":0,
"masterId":null,
"seats":4
},
{
...
}
]
}
My assignment object looks like this:
[ Assignment {
id: 6798,
tableId: 685,
guestGroupId: 60725,
start_timestamp: undefined,
end_timestamp: undefined },
Assignment {...}
]
The idea is to do an assignment lookup in your map
It's really the only way. You don't want to be nesting loops of different data sets.
const newLayouts = layouts.map(layout =>
layout.tables.map(table => {
const assignment = assignments.find(({ id }) => id === table.id);
if (
assignment &&
assignment.start_timestamp >= timestamp &&
!assignment.end_timestamp < timestamp
) {
return {
...table,
translateY: null,
translateX: null,
masterId: null
};
}
// Else
return table;
})
);

Filter and reduce object in javascript

I am not really sure on how to do this, I have an object that contains several objects. It looks like this :
object {
apps: {
apps1: {
date: "2017-07-05T17:13:53.956Z",
status: false
}
},
apps2: {
date: "2017-07-11T15:15:39.027Z",
status: false
},
apps3: {
date: "2017-07-11T15:36:33.063Z",
status: true
}
}
Now I wanted to sort them by date first which is working :
function getAppStatus(user) {
let newArr = Object.keys(user.apps).reduce(function(a, b) {
return user.apps[a].date > user.apps[b].date ? a : b;
});
But I only want to sort the one that have status === false so basically I want to delete the apps3 if the status is true and then sort the rest by date.
Any ideas ?
thanks !
Try something like
var newArr = Object.keys(user.apps)
// convert the object to an array by key.
.map(function (key) { return user.apps[key]; })
// get apps with status === false.
.filter(function (app) { return !app.status; })
// sort by date. Since the date is stored as a string, use localeCompare.
.sort(function (a1, a2) { return a1.date.localeCompare(a2.date)); });

Simple method to modify json keys casing to camelCase from snake_case

I have lots of json parsing and all the json I am receiving is having keys in snake case like user_name. And for parsing I need it in camel case like userName.
The sample json file would look like this:
[{
"user_id" : 1,
"user_name" : "Abcd"
},
{
"org_id" : 11,
"org_name" : "some_name"
}
...
]
Expected output:
[{
"userId" : 1,
"userName" : "Abcd"
},
{
"orgId" : 11,
"orgName" : "some_name"
}
...
]
The json I am receiving is not having any particular fixed notation and it can be anything. But all the keys will be in snake case. And I need to convert it to camelCase.
What I cannot do is, find and replace, because it also replace snake casing strings in values as well.
Is there any easy method, which can do the same?
You can use npm package called: camelcase-keys-deep
https://www.npmjs.com/package/camelcase-keys-deep
You can do the following:
var keys = [];//this will contain the json with desired output
for(var i = 0;i<myObj.length;i++)//myObj is the variable that contains your json
{
Object.keys(myObj[i]).forEach(function(key){
if(keys.indexOf(key) == -1)
{
var newValue = {};
var value = myObj[i][key];
key = key.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });
newValue[key] = value;
keys.push(newValue);
}
});
}
//console.log(keys);
Hope this helps :)
So AKSHAY JAIN's implementation is pretty solid but it will delete the properties that are not in snake case. I fixed the implementation.
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
},
{
"personId": 12,
"personName": "otherName"
}];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
if (newK != k) {
a[newK] = a[k];
delete a[k];
}
});
});
console.log(arr);
If u use lodash I would suggest my solution for this:
snake_case -> camelCase
function camelCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => camelCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.camelCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => camelCaseDeep(value));
return objWithMappedValues;
}
camelCase -> snake_case
function snakeCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => snakeCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.snakeCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => snakeCaseDeep(value));
return objWithMappedValues;
}
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
}
];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
a[newK] = a[k];
delete a[k];
});
});
console.log(arr);

Categories