UnderscoreJs: _.groupBy together with _.sortBy - javascript

I'm trying to use underscores _.groupBy() and _.sortBy in pair, and a problem is that the last one changes object with keys returned from groupBy to array with indexes. Is it possible to preserve original indexes (keys) from object?
Here is example:
My code:
var sorted = _.chain(cars).groupBy('Make').sortBy(function(car) {
return car.length * -1;
});
Result from groupBy:
{
"Volvo" : [ "S60", "V40" ],
"Volkswagen" : [ "Polo", "Golf", "Passat" ]
}
Result from sortBy:
[
0 : [ "Polo", "Golf", "Passat" ],
1 : [ "S60", "V40" ]
]
Expected result:
[
"Volkswagen" : [ "Polo", "Golf", "Passat" ],
"Volvo" : [ "S60", "V40" ]
]

Objects are unordered in JavaScript. If you need something like an object but ordered, you can use _.pairs to convert it, then sort the list of pairs.
_.pairs({
"Volvo" : [ "S60", "V40" ],
"Volkswagen" : [ "Polo", "Golf", "Passat" ]
})
Gives:
[
["Volvo", [ "S60", "V40" ]],
["Volkswagen", [ "Polo", "Golf", "Passat" ]]
]
...which you can then sort using _.sortBy. If you assign the above to cars, then:
_.sortBy(cars, function(x) { return -x[0].length; });
gives:
[
[ 'Volkswagen', ['Polo', 'Golf', 'Passat' ]],
[ 'Volvo', ['S60', 'V40']]
]

Related

Is there any library or short way in Javascript for map nested arrays?

I have an array like this.
let Array = [{
id: 1,
name: "Car",
cars: [{
id: 2,
name: "Hyundai",
models: [
"Verna", "Aura", "Azera", "Accent", "Sonata"
]
}]
}....]
And I think this not the best way: Array.map(car => car.cars.map(model => model.models)).
Is there any other way?
There is no 'short way' as what you are doing is very specific to your problem. The way you wrote it is the most concise if you want to end up with an array like:
let Array = [
[
[ "Verna", "Aura", "Azera", "Accent", "Sonata" ],
[ ... ],
],
[ ... ],
]
But if you want to flatten the array, you can consider writing it as:
Array
.map(car => car.cars).flat()
.map(model => model.models).flat()
This will result in an array like this:
[ "Verna", "Aura", "Azera", "Accent", "Sonata", ... ]
Notes
Do not call an array Array as is the name of the array constructor. You will lose the ability to do new Array().

Flatten an array grouped by nested elements in JavaScript

I have a tricky question... I have an array looks like this:
[
[ [ 'Attribute1', 'Attribute1Synonym1' ], [ 'Attribute2' ] ],
[ [ 'Attribute3' ] ],
[ [ 'Attribute2' ] ]
]
My result should be:
[
'Attribute1 Attribute2',
'Attribute1Synonym1 Attribute2',
'Attribute3',
'Attribute2'
]
The tricky thing is:
the result array has to grouped by the sub-sub-array
the crux is, the first index is an array(1) of arrays(2) of arrays(3)
and i would to like flatten the array by level 3 (array(3)) and at the result should be every possible combination between the upper level.
At level 2 (the first index) is an array with ('Attribute1' and 'Attribute1Synonym1')
so the result should be:
'Attribute1 Attribute2'
and
'Attribute1Synonym1 Attribute2'
the 'Attribute2' comes from the upper level
if the second index of level 2 ['Attribute2'] has also multiple indexes
for example ['Attribute2Synonym5']
the result should be:
'Attribute1 Attribute2'
'Attribute1Synonym1 Attribute2'
'Attribute1 Attribute2Synonym5'
'Attribute1Synonym1 Attribute2Synonym5'
and so on...
This works against your provided example, but I'm going to guess it's fragile against more complex arrays:
const deep = [ [ [ 'Attribute1', 'Attribute1Synonym1' ], [ 'Attribute2' ] ],
[ [ 'Attribute3' ] ],
[ [ 'Attribute2' ] ] ];
const flat = [];
deep.forEach(element => {
const left = element[0];
const right = element[1];
left.forEach(leftElement => {
if(right){
right.forEach(rightElement => {
flat.push(leftElement + ' ' + rightElement);
});
} else {
flat.push(leftElement);
}
})
});
Maybe like this:
var input_arr=[ [ [ 'Attribute1', 'Attribute1Synonym1' ], [ 'Attribute2' ] ],
[ [ 'Attribute3' ] ],
[ [ 'Attribute2' ] ] ];
var output_arr=[];
for(var key1 in input_arr){
var sub_input_arr=input_arr[key1];
for(var key2 in sub_input_arr){
var sub_sub_input_arr=sub_input_arr[key2];
for(var key3 in sub_sub_input_arr){
output_arr.push(sub_sub_input_arr[key3]);
}
}
}
console.log(output_arr);

Mapping values from two arrays

I am using a text analysis service (pos) which I can pass a string at it tells me whether than string contains verbs, nouns etc.
I have code:
var words = new pos.Lexer().lex(req.body.Text);
var tagger = new pos.Tagger();
var taggedWords = tagger.tag(words);
taggedWords is then passed to a handlebars template and looped through and printed.
If I console.log(taggedWords) I see a multidimensional array eg:
[
[ 'Interest-only', 'RB' ],
[ 'deals', 'NNS' ],
[ 'allow', 'VB' ],
[ 'only', 'RB' ],
[ 'ends', 'NNS' ],
...
...
]
I would like to maintain a separate array which maps the values in the above array to human-readable version:
[
['RB', 'adjective'],
['NNS', 'noun'],
['VB', 'verb'],
...
...
]
and then be able to rewrite so that the original array (taggedWords) looks like:
[
[ 'Interest-only', 'adjective' ],
[ 'deals', 'noun' ],
[ 'allow', 'verb' ]
]
and then pass this new array to my template. What is the most efficient way to do this?
var taggedWords = [
[ 'Interest-only', 'RB' ],
[ 'deals', 'NNS' ],
[ 'allow', 'VB' ],
[ 'only', 'RB' ],
[ 'ends', 'NNS' ]
];
var dico = {
'RB' : 'adjective',
'NNS' : 'noun',
'VB' : 'verb'
};
taggedWords.forEach( elt => { elt[1] = dico[elt[1]] });
console.log(taggedWords);
You can use map() to create a new array with modified elements from your original. This code changes the second item in each tagged word to what is listed in the dictionary for that tag.
let taggedWords = [
[ 'Interest-only', 'RB' ],
[ 'deals', 'NNS' ],
[ 'allow', 'VB' ],
[ 'only', 'RB' ],
[ 'ends', 'NNS' ]
];
let dict = [
['RB', 'adjective'],
['NNS', 'noun'],
['VB', 'verb']
];
let result = taggedWords.map(tag => {
tag[1] = dict.find(item => item[0] === tag[1])[1];
return tag;
});
console.log(result);
Brute force method would involve going through all the elements in the arrays and find a match for them in another array and push them into a third array. This will require that you don't have the same tag word in an array twice; ie: 'RB', or 'NNS'. Hope this solves your problem for now. The benefits to this method as apposed to the previous answer would be that the order of items in arrays don't matter since you're comparing each element to every other element in the other array.
let array1 = [
[ 'Interest-only', 'RB' ],
[ 'deals', 'NNS' ],
[ 'allow', 'VB' ],
[ 'only', 'RB' ],
[ 'ends', 'NNS' ]
];
let array2 = [
['RB', 'adjective'],
['NNS', 'noun'],
['VB', 'verb'],
];
let array3 = [];
array1.forEach(el =>
{
array2.forEach(par =>
{
if (el[1] === par[0])
{
array3.push([el[0], par[1]])
}
})
});
console.log(array3);

Default values in json object based on contents of other objects in the same array

I'm pretty lost right now and have been working on this for about 6 days now so forgive me if this is a bit confusing. I'm using NVD3 to display some graphs based on data that comes in from BigQuery. All the data coming in is correct and so is the graph implementation, the issue is the actual JSON data. The multi bar chart requires that each "set" of data have the same dates and same number of values under the initial array. Based off my data, sometimes there will be missing values if a user didn't log an event or something that day.
The general idea of what I'm trying to do here is loop through the initial json and append on the "missing" values. For example this would be the initial data that I get out of BigQuery and my API:
[
"t1":{
"target": "t1",
"datapoints": [
[
16.0,
1483747200.0
],
[
10.0,
1484352000.0
]
]
},
"t2":{
"target": "t2",
"datapoints": [
[
10.0,
1483660800.0
],
[
19.0,
1484006400.0
],
[
10.0,
1484956800.0
]
]
}
]
You can see here that the first object has a datapoints array with 2 values, object two has a datapoints array with 3 values. The 1 index of the datapoints array contains a UNIX date, every datapoints array within the entire object must have an array with the date and then 0 for a default value. So the formatted data would look something like this:
[
"t1":{
"target": "t1",
"datapoints": [
[
16.0,
1483747200.0
],
[
10.0,
1484352000.0
],
[
0.0,
1483660800.0
],
[
0.0,
1484006400.0
],
[
0.0,
1484956800.0
]
]
},
"t2":{
"target": "t2",
"datapoints": [
[
10.0,
1483660800.0
],
[
19.0,
1484006400.0
],
[
10.0,
1484956800.0
],
[
0.0,
1483747200.0
],
[
0.0,
1484352000.0
]
]
}
]
I really have no idea where to go from here and any help whatsoever would be extremely helpful. I've been working on this for days and at this point am just grinding my gears. Thanks
Basically, each value that's found in one array but not in others should take the timestamp but set the first value/index to 0.
I should also mention that the query is only querying for 30 days back so at most each one of the datapoints arrays would have 30 arrays.
You will have to do a bit of data processing first to get all of the dates, then it's just a matter of filling in the actual data for each date.
const json = [
{
"target": "t1",
"datapoints": [
[
16.0,
1483747200.0
],
[
10.0,
1484352000.0
]
]
},
{
"target": "t2",
"datapoints": [
[
10.0,
1483660800.0
],
[
19.0,
1484006400.0
],
[
10.0,
1484956800.0
]
]
}
]
// using es6 set
const dates = new Set()
json.forEach( x => x.datapoints.map( dp => dates.add(dp[1]) ) )
// all dates are there
dates.forEach( d => console.log(d) )
const fillDp = dp => {
return Array.from(dates).sort().map( d => dp.find( x => x[1] === d ) || [0,d] )
}
const result = json.map( x => Object.assign(x, {datapoints: fillDp(x.datapoints)}) )
console.log(JSON.stringify(result[0].datapoints))
Below is a quick and dirty solution. Note that I added an additional entry to the sample you provided so that both data objects contain a data point with a common date.
var orig= [
{
"target": "t1",
"datapoints": [
[
16.0,
1483747200.0
],
[
10.0,
1484352000.0
],
[
10.0,
1483660800.0
]
]
},
{
"target": "t2",
"datapoints": [
[
10.0,
1483660800.0
],
[
19.0,
1484006400.0
],
[
10.0,
1484956800.0
]
]
}
];
console.log('Original Data', JSON.stringify(orig));
// Get a list of all the datapoint dates
var dates = [];
orig.forEach(function(item) {
item.datapoints.forEach(function(dp) {
var date = dp[1];
dates.push(date);
})
});
console.log('All Dates', JSON.stringify(dates));
// Remove duplicates from array
dates = dates.filter(function (el, i, arr) {
return arr.indexOf(el) === i;
});
console.log('Unique Dates', JSON.stringify(dates));
// Check each item in the original array for records for each date and add a
// 0 value entry if the date entry is missing
dates.forEach(function(dt) {
orig.forEach(function(item, itemIndex) {
var hasEntry = false;
item.datapoints.forEach(function(dp) {
if (dp[1] === dt) hasEntry = true;
});
if (!hasEntry) {
item.datapoints.push([0, dt]);
}
});
});
console.log('Updated Data', JSON.stringify(orig));
And here is the corresponding plunker so you can see it in action: https://plnkr.co/edit/K1NK2Xx8RNrqyp7yZ3n2?p=preview

jQuery: Create an array from JSON

I have a JSON like this:
{
"default": [
[
1325876000000,
0
],
[
1325876000000,
0
],
[
1325876000000,
0
],
[
1325876000000,
0
]
],
"direct": [
[
1328196800000,
0
],
[
1328196800000,
100
],
[
1328196800000,
0
],
[
1328196800000,
0
]
],
"Sales": [
[
1330517600000,
0
],
[
1330517600000,
0
],
[
1330517600000,
90
],
[
1330517600000,
0
]
],
"Support": [
[
1332838400000,
0
],
[
1332838400000,
0
],
[
1332838400000,
0
],
[
1332838400000,
0
]
]
}
I want to generate array contains the name of each item and the first value of the corresponing array. the result should be like this:
ticks = [["default", 1325876000000],["direct", 1328196800000],["Sales", 1330517600000],["Support", 1332838400000]]
the names like default, direct, sales, supportare dynamic so I can't do jsondata.support
what I tried
ticks = []
for key in jsondata{
arraynew = [];
arraynew.push(key)
}
but I don't know how to push the values?
Help please.
You just need to access the sub-array.
var ticks = [];
for (var key in jsondata) {
ticks.push( [ key, jsondata[key][0][0] ] );
}
The expression jsondata[key] gets you the outer array corresponding to each key. Then, jsondata[key][0] gets you the first of the sub-arrays, and adding the final [0] to that gets you the first value in the first sub-array.
Note that you're not guaranteed to get the keys back in any particular order.

Categories