Create Object from Array and sort - javascript

i've got an Array like this
(1) var values = [2014.02.01", "2014.03.01", "2014.03.01", "2014.03.01", "2014.03.01", "2014.03.17", "2014.04.01", "2014.04.01", "2014.04.09",...];
and i want to count the dates and create a object in that format
(2) count = {01.02.2014: 1, 01.03.2014: 4, 17.03.2014: 1, 01.04.2014: 2, 09.04.2014: 1,...}
i tried it in that way but this don't sort the object in the wright way
(3) values.forEach(function(i) { count[i] = (count[i]||0)+1; });
the result isn't sorted...is there a way to create a object with sorted dates? I already sort the array before creating the object but it doesn't work
edit:
reversing dates is no problem
for (var j=0; j<values.length; j++){
values[j] = values[j].split(".").reverse().join(".");
}
but creating the object that is sorted by date is the problem...my way (3) doesn't sort the object

// group the data by date and count the appearances
var grouped = values.reduce(function(l, r) {
l[r] = (l[r] || 0) + 1;
return l;
}, {});
// as objects can't be sorted, map it to an array of objects with the desired data as properties
var groupedAndSorted = Object.keys(grouped).map(function(key) {
return { "date": key, "count": grouped[key] };
}).sort(function(a, b) {
return a.date > b.date;
});
// [{"date":"2014.02.01","count":1},{"date":"2014.03.01","count":4},{"date":"2014.03.17","count":1},{"date":"2014.04.01","count":2},{"date":"2014.04.09","count":1}]

You can do it by following the functional way which tends to be more sexy, shorter. To handle this, you can simulate an hashmap key/value.
You can use Array.prototype.reduce() which is a powerful method.
//Reduce our data and initialize our result to an empty object
//Sort values input and perform reduce
var count = values.sort().reduce(function(result, current){
//Format our key
var key = current.split('.').reverse('').join('.');
//Set value to our object
result[key] = (++result[key] || 1);
return result;
}, {});
console.log(count);
EDIT
I've had key formatting for out object.

You should sort the dates using a compare function that simply strips the periods before rearranging them:
var arr = ["2014.02.01", "2014.03.01", "2014.03.01", "2014.03.01", "2014.03.01", "2014.03.17", "2014.04.01", "2014.04.01", "2014.04.09"]
arr.sort(function(a, b) {
return (a.replace('.','') - b.replace('.',''));
}).forEach(function(v,i){arr[i] = v.split('.').reverse().join(".")});
document.write(arr.join('<br>'));
All thats required next is to turn the array into an object with a count of the recurring dates, which can also reverse the values in each string:
arr = arr.sort(function(a, b) {
return (a.replace('.','') - b.replace('.',''));
}).reduce(function(obj, v){
v = v.split('.').reverse().join('.')
if (!obj.hasOwnProperty(v)) {
obj[v] = 0;
}
++obj[v];
return obj;
},{});
But be warned, there is no guarantee that you'll get consistent ordering. In the latest version of the language specification, order is based firstly on numeric order if the property is a number, then the order properties are added. The Safari console thinks the keys are numbers so returns the following order:
Object { 01.02.2014: 1, 01.03.2014: 4, 01.04.2014: 2, 09.04.2014: 1, 17.03.2014: 1 }
but when stringified with JSON.stringify or the keys are returned by Object.keys, they're in the same order as Firefox and Chrome which return:
Object { 01.02.2014: 1, 01.03.2014: 4, 17.03.2014: 1, 01.04.2014: 2, 09.04.2014: 1 }
which is consistent with ECMAScript 2015.
To get consistent ordering, you should use ISO 8601 like dates similar to the original but without the periods. But best of all, you should not expect the order to be consistent or predictable, because it across browsers in use (unless you restrict yourself to one particular implementation that does what you want).

Related

JavaScript loop, create object, but shows zero in length

I have a javascript method, where I'm trying to group my data by 'date', which works fine.
But when I look at the length of my object it shows, 'zero'.
Is there a better way to do this? and keep the length of the new variable?
groupByDate(snapshot) {
let list = []
this.orders.reduce(function (a, c) {
if(!list[c.date]) {
list[c.date] = [];
}
list[c.date].push(c);
});
this.ordersByDate = list
console.log(list)
}
You are pushing properties into an array. You are not setting the indexes of the array. So you should be using an object and not an array. Your use of reduce is also not correct. You are treating it like a forEach loop.
So use an object and use reduce the way it is supposed to be
let list = this.orders.reduce(function (o, c) {
o[c.date] = o[c.date] || []
o[c.date].push(c)
return o
}, {})
console.log(Object.keys(list).length)
I would use a dictionary (key, values) for that:
list {
date1: [obj1-1, obj1-2, ...],
date2: [obj2-1, obj2-2, ...],
...
}
list = {};
this.orders.forEach(order => {
if (!list.hasOwnProperty(order.date)) {
// If it is the first time we meet this date, create an array with th first element
list[order.date] = [order];
} else {
// We have already meet the date, thus the key exists and so do the array. Add element to array
list[order.date].push(order);
}
});
It seems you have an array and its indexes are the dates. I assume the dates are big numbers (date from 1970 in milliseconds, something like that), which may lead to having a very very big array 99,9% empty. That is why according to me you should use an object and not an array.
Or maybe those are not dates but id's of dates ?

Javascript - Get Array of indices for 'WILL BE' relationship after sort

I have a very simple question. Say I have an array
a = [10,40,30,20,60,50]
After sorting, it would be (assuming I use sort_a = a.sort())
sort_a = [60,50,40,30,20,10]
I want to create an array of indices from a which specify which location in the sorted array that element WILL BE after sorting. From the above example, the result would be
a_sortedindices = [6, 3, 4, 5, 1, 2]
..meaning 10 is in the 6th position when sorted, 40 is in the 3rd position... etc
Pair the values with their current indices
Sort the array of pairs based on the original values
Combine the pairs with their new indices
Sort the new array based on the original indices
Obtain the new indices from the sorted array
let values = [10,40,30,20,60,50];
let indices = values
.map((v, i) => ({ v, i }))
.sort((l, r) => r.v - l.v)
.map(({v, i}, i2) => ({ v, i, i2 }))
.sort((l, r) => l.i - r.i)
.map(p => p.i2);
console.log(indices);
This results in an array of 0-based indices because JavaScript uses 0-based indices. If you want 1-based indices like in your question, you can change p.i2 to p.i2 + 1 in the second to last line.
One of the ways, apart from many to achieve this:
1) Transform the array into another with old indices
2) Sort the array in descending order
3) Create an answer array since you now know the old and new indices.
let a = [10,40,30,20,60,50];
let transformed = a.map((v,i)=> {
return {num:v,old:i};
});
transformed.sort((a,b)=> {
return b.num - a.num;
});
let ans = [];
transformed.forEach((v,i) => {
ans[v.old] = i+1;
});
console.log(ans);
Not sure if this is a trick question or if you're trying to find the most minimal method for achieving this, but you basically already have it. This is what I came up with:
var a = [10,40,30,20,60,50];
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
console.log(a_sortedindices);
Walking through it, I'll explain each part.
First, off you have to sort it. Looks like you need reverse sorting, so we'll add an arrow function describing a reverse sort, but before we do that, we'll also clone the array, otherwise we'll lose the original indexes of the values. .slice(0) is a nice way to return a clone of an array
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
Then we'll map each value of the origin array. .map() is nice and easy to quickly manipulate each element in an array. We use .indexOf() to figure out where it was at in the original array. We add one to that value because you're not using zero-based indexing.
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
And voila. You have the sorted indexes.
A naive way of doing this job could be;
var arr = [10,40,30,20,60,50],
idx = arr.map(function(n){return this.indexOf(n)+1;}, arr.slice().sort((a,b) => b-a));
console.log(idx);
where the this argument for the .map() function callback is arr.slice().sort((a,b) => b-a)
// the array to be sorted
var list = [10,20,30,40];
// temporary array holds objects with position and sort-value
var mapped = list.map(function(el, i) {
return { index: i, value: el };
})
// sorting the mapped array
mapped.sort(function(a, b) {
return b.value - a.value;
});
// you can then remap the sorted mapped array to hold just the indices
P.S.: For future reference MDN is your friend

Get property with oldest date

I'm in a situation where I need to store some data in an object, but I can only have a set number of that data due to browser limitations. Since my app also needs to be able to get this data, I am storing it in an object where the keys of the properties are identifiers.
The data looks like this:
memory = {
13: {
last_updated: 241,
...
},
26: {
last_updated: 363,
....
}
}
last_updated would be a Date.now() string of course. This object can not have more than 6 properties. When it reaches that length, I need to start replacing the oldest properties with new data. How do I get the oldest property of the object?
One way would be to just sort the objects keys by the last updated timestamp, and pop of the last one, the oldest one
var oldest = memory[Object.keys(memory).sort(function(a,b) {
return memory[b].last_updated - memory[a].last_updated
}).pop()];
You can do something like this:
var obj = {};
for(var i = 0;i < 5; i++){
obj[i] = {date:Date.now() + i};
}
function addNew(date){
if(obj.length < 5){
obj[obj.length] = {date:date};
return;
}
var arr = [];
for(var prop in obj){
arr.push({prop:prop,date:obj[prop].date})
}
var last = arr.sort(function(a, b){return b.date-a.date})[0].prop;
obj[last].date = date;
}
addNew(Date.now() + 100);
console.log(obj);
You need to add new properties to the object only by using the addNew(...) function in order for this to work.
This would take the newest 6 items from the memory object.
var freshMemory = Object.keys(memory)
.map(function (k) { return [k, data[k]] })
.sort(function (a, b) { return b[1].last_updated - a[1].last_updated; })
.slice(0, 6)
.reduce(function (o, v) { o[v[0]] = v[1]; return o; }, {});
A short explanation
The Object.keys combined with the .map method will transfer the object to an array containing two-tuples (the key and the value)
then the .sort will sort the array by last_updated in reverse order
then the .slice will take the first 6 items
and finally .reduce will convert the array with two-tuples back to an object.

How to get the last item in an object by sorted key order

Suppose I have an object with this structure:
{
"friends_count": {
"1420800660": 49391,
"1421149814": 49344,
"1421149955": 49344
}
}
In the object, the first number (the key) is a timestamp. The second number is the value. I want to get the most recent item of that object. So, I need to get the key that is closest in time. How do I have to do it?
So, I need to get the key that is closest in time
Sure. Just call Object.keys on obj.friends_count and then sort
var key = Object.keys(obj.friends_count).sort()[0];
Object.keys returns an array of keys of the provided object and Array.sort will sort it in ascending order and [0] will take the first element of the sorted array.
Just Array.sort will work fine here since they are of the same length and everything should be fine. If you want to be more clear, then it would be arr.sort(function(a, b){ return a - b })[0]
If you are wanting to get the latest from the key timestamp then just convert it to a date:
var something = {
"friends_count": {
"1420800660": 49391,
"1421149814": 49344,
"1421149955": 49344
}
},
dateCheck,
newest;
for(var key in something.friends_count){
if(!dateCheck || new Date(key*1000) > dateCheck){
dateCheck = new Date(key*1000);
newest = something.friends_count[key];
}
}
console.log(newest);
http://jsfiddle.net/w4dsk4m3/

Javascript array sort and unique

I have a JavaScript array like this:
var myData=['237','124','255','124','366','255'];
I need the array elements to be unique and sorted:
myData[0]='124';
myData[1]='237';
myData[2]='255';
myData[3]='366';
Even though the members of array look like integers, they're not integers, since I have already converted each to be string:
var myData[0]=num.toString();
//...and so on.
Is there any way to do all of these tasks in JavaScript?
This is actually very simple. It is much easier to find unique values, if the values are sorted first:
function sort_unique(arr) {
if (arr.length === 0) return arr;
arr = arr.sort(function (a, b) { return a*1 - b*1; });
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) { //Start loop at 1: arr[0] can never be a duplicate
if (arr[i-1] !== arr[i]) {
ret.push(arr[i]);
}
}
return ret;
}
console.log(sort_unique(['237','124','255','124','366','255']));
//["124", "237", "255", "366"]
You can now achieve the result in just one line of code.
Using new Set to reduce the array to unique set of values.
Apply the sort method after to order the string values.
var myData=['237','124','255','124','366','255']
var uniqueAndSorted = [...new Set(myData)].sort()
UPDATED for newer methods introduced in JavaScript since time of question.
This might be adequate in circumstances where you can't define the function in advance (like in a bookmarklet):
myData.sort().filter(function(el,i,a){return i===a.indexOf(el)})
Here's my (more modern) approach using Array.protoype.reduce():
[2, 1, 2, 3].reduce((a, x) => a.includes(x) ? a : [...a, x], []).sort()
// returns [1, 2, 3]
Edit: More performant version as pointed out in the comments:
arr.sort().filter((x, i, a) => !i || x != a[i-1])
function sort_unique(arr) {
return arr.sort().filter(function(el,i,a) {
return (i==a.indexOf(el));
});
}
How about:
array.sort().filter(function(elem, index, arr) {
return index == arr.length - 1 || arr[index + 1] != elem
})
This is similar to #loostro answer but instead of using indexOf which will reiterate the array for each element to verify that is the first found, it just checks that the next element is different than the current.
Try using an external library like underscore
var f = _.compose(_.uniq, function(array) {
return _.sortBy(array, _.identity);
});
var sortedUnique = f(array);
This relies on _.compose, _.uniq, _.sortBy, _.identity
See live example
What is it doing?
We want a function that takes an array and then returns a sorted array with the non-unique entries removed. This function needs to do two things, sorting and making the array unique.
This is a good job for composition, so we compose the unique & sort function together. _.uniq can just be applied on the array with one argument so it's just passed to _.compose
the _.sortBy function needs a sorting conditional functional. it expects a function that returns a value and the array will be sorted on that value. Since the value that we are ordering it by is the value in the array we can just pass the _.identity function.
We now have a composition of a function that (takes an array and returns a unique array) and a function that (takes an array and returns a sorted array, sorted by their values).
We simply apply the composition on the array and we have our uniquely sorted array.
This function doesn't fail for more than two duplicates values:
function unique(arr) {
var a = [];
var l = arr.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++) {
// If a[i] is found later in the array
if (arr[i] === arr[j])
j = ++i;
}
a.push(arr[i]);
}
return a;
};
Here is a simple one liner with O(N), no complicated loops necessary.
> Object.keys(['a', 'b', 'a'].reduce((l, r) => l[r] = l, {})).sort()
[ 'a', 'b' ]
Explanation
Original data set, assume its coming in from an external function
const data = ['a', 'b', 'a']
We want to group all the values onto an object as keys as the method of deduplication. So we use reduce with an object as the default value:
[].reduce(fn, {})
The next step is to create a reduce function which will put the values in the array onto the object. The end result is an object with a unique set of keys.
const reduced = data.reduce((l, r) => l[r] = l, {})
We set l[r] = l because in javascript the value of the assignment expression is returned when an assignment statement is used as an expression. l is the accumulator object and r is the key value. You can also use Object.assign(l, { [r]: (l[r] || 0) + 1 }) or something similar to get the count of each value if that was important to you.
Next we want to get the keys of that object
const keys = Object.keys(reduced)
Then simply use the built-in sort
console.log(keys.sort())
Which is the set of unique values of the original array, sorted
['a', 'b']
The solution in a more elegant way.
var myData=['237','124','255','124','366','255'];
console.log(Array.from(new Set(myData)).sort((a,b) => a - b));
I know the question is very old, but maybe someone will come in handy
A way to use a custom sort function
//func has to return 0 in the case in which they are equal
sort_unique = function(arr,func) {
func = func || function (a, b) {
return a*1 - b*1;
};
arr = arr.sort(func);
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (func(arr[i-1],arr[i]) != 0)
ret.push(arr[i]);
}
}
return ret;
}
Example: desc order for an array of objects
MyArray = sort_unique(MyArray , function(a,b){
return b.iterator_internal*1 - a.iterator_internal*1;
});
No redundant "return" array, no ECMA5 built-ins (I'm pretty sure!) and simple to read.
function removeDuplicates(target_array) {
target_array.sort();
var i = 0;
while(i < target_array.length) {
if(target_array[i] === target_array[i+1]) {
target_array.splice(i+1,1);
}
else {
i += 1;
}
}
return target_array;
}
I guess I'll post this answer for some variety. This technique for purging duplicates is something I picked up on for a project in Flash I'm currently working on about a month or so ago.
What you do is make an object and fill it with both a key and a value utilizing each array item. Since duplicate keys are discarded, duplicates are removed.
var nums = [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10];
var newNums = purgeArray(nums);
function purgeArray(ar)
{
var obj = {};
var temp = [];
for(var i=0;i<ar.length;i++)
{
obj[ar[i]] = ar[i];
}
for (var item in obj)
{
temp.push(obj[item]);
}
return temp;
}
There's already 5 other answers, so I don't see a need to post a sorting function.
// Another way, that does not rearrange the original Array
// and spends a little less time handling duplicates.
function uniqueSort(arr, sortby){
var A1= arr.slice();
A1= typeof sortby== 'function'? A1.sort(sortby): A1.sort();
var last= A1.shift(), next, A2= [last];
while(A1.length){
next= A1.shift();
while(next=== last) next= A1.shift();
if(next!=undefined){
A2[A2.length]= next;
last= next;
}
}
return A2;
}
var myData= ['237','124','255','124','366','255','100','1000'];
uniqueSort(myData,function(a,b){return a-b})
// the ordinary sort() returns the same array as the number sort here,
// but some strings of digits do not sort so nicely numerical.
function sort() only is only good if your number has same digit, example:
var myData = ["3","11","1","2"]
will return;
var myData = ["1","11","2","3"]
and here improvement for function from mrmonkington
myData.sort().sort(function(a,b){return a - b;}).filter(function(el,i,a){if(i==a.indexOf(el) & el.length>0)return 1;return 0;})
the above function will also delete empty array and you can checkout the demo below
http://jsbin.com/ahojip/2/edit
O[N^2] solutions are bad, especially when the data is already sorted, there is no need to do two nested loops for removing duplicates. One loop and comparing to the previous element will work great.
A simple solution with O[] of sort() would suffice. My solution is:
function sortUnique(arr, compareFunction) {
let sorted = arr.sort(compareFunction);
let result = sorted.filter(compareFunction
? function(val, i, a) { return (i == 0 || compareFunction(a[i-1], val) != 0); }
: function(val, i, a) { return (i == 0 || a[i-1] !== val); }
);
return result;
}
BTW, can do something like this to have Array.sortUnique() method:
Array.prototype.sortUnique = function(compareFunction) {return sortUnique(this, compareFunction); }
Furthermore, sort() could be modified to remove second element if compare() function returns 0 (equal elements), though that code can become messy (need to revise loop boundaries in the flight). Besides, I stay away from making my own sort() functions in interpreted languages, since it will most certainly degrade the performance. So this addition is for the ECMA 2019+ consideration.
The fastest and simpleness way to do this task.
const N = Math.pow(8, 8)
let data = Array.from({length: N}, () => Math.floor(Math.random() * N))
let newData = {}
let len = data.length
// the magic
while (len--) {
newData[data[len]] = true
}
var array = [2,5,4,2,5,9,4,2,6,9,0,5,4,7,8];
var unique_array = [...new Set(array)]; // [ 2, 5, 4, 9, 6, 0, 7, 8 ]
var uniqueWithSorted = unique_array.sort();
console.log(uniqueWithSorted);
output = [ 0, 2, 4, 5, 6, 7, 8, 9 ]
Here, we used only Set for removing duplicity from the array and then used sort for sorting array in ascending order.
I'm afraid you can't combine these functions, ie. you gotta do something like this:-
myData.unique().sort();
Alternatively you can implement a kind of sortedset (as available in other languages) - which carries both the notion of sorting and removing duplicates, as you require.
Hope this helps.
References:-
Array.sort
Array.unique

Categories

Resources