Sort data 'Abr/2017' - javascript

I need to sort my array, he is like this:
x = {
'Abr/2017': [ { id: 1 } ],
'Fev/2018': [ { id: 1 } ],
'Jul/2017': [ { id: 1 } ],
'Abr/2018': [ { id: 1 } ],
'Fev/2017': [ { id: 1 } ],
'Jul/2018': [ { id: 1 } ],
'Dez/2019': [ { id: 1 } ]
}
and I need him to be sorted first in the year and then in the months

Because your data is represented as object, you have to convert it to array as a first step. Then you can sort. Later you can map result array to whatever you want.
Important thing is that if you want to keep this data in some order then you have to use array structure
let x = {
'Abr/2017': [ { id: 1 } ],
'Fev/2018': [ { id: 1 } ],
'Jul/2017': [ { id: 1 } ],
'Abr/2018': [ { id: 1 } ],
'Fev/2017': [ { id: 1 } ],
'Jul/2018': [ { id: 1 } ],
'Dez/2019': [ { id: 1 } ]
}
// this function transform your object to array representation
// where your actual key (e.g. Fev/2017) is stored in helper
// property called key
function toArray(obj) {
return Object.keys(obj).reduce((arr, key) => {
arr.push({ key, data: obj[key] })
return arr
}, [])
}
function byYearAndMonth() {
// month definition - help with sorting
let monthOrder = {
'Jan': 1,
'Fev': 2,
'Mar': 3,
'Abr': 4,
'Mai': 5,
'Jun': 6,
'Jul': 7,
'Ago': 8,
'Set': 9,
'Out': 10,
'Nov': 11,
'Dez': 12
}
let mapDate = function([month, year]) {
return [monthOrder[month], Number(year)]
}
// actual sorting function
return function(a, b) {
const [aMonth, aYear] = mapDate(a.key.split('/'))
const [bMonth, bYear] = mapDate(b.key.split('/'))
if(aYear < bYear) {
return -1
}
if(aYear > bYear) {
return 1
}
if(aMonth < bMonth) {
return -1
}
if(aMonth > bMonth) {
return 1
}
return 0
}
}
// lets try how it works
let xArray = toArray(x)
xArray.sort(byYearAndMonth());
var result = xArray.map(x => x)
// with map (or reduce) you can transform it to whatever you want
// I'm just returning exactly the same object
console.log(result)

Related

Group the parent object array based on the filter of the child object array

I've coded and I've got the results I want, but I think this code looks unclean.
const verses = [{
index: 1,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 2,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 3,
verses: [{
level: 2
}]
},
{
index: 4,
verses: [{
level: 2
}]
},
{
index: 5,
verses: [{
level: 2
},
{
level: 2
}
]
},
{
index: 6,
verses: [{
level: 3
},
{
level: 3
}
]
},
{
index: 7,
verses: [{
level: 3
},
{
level: 3
},
{
level: 4
},
{
level: 4
}
]
}
]
function getVerseIndexByLevel(level) {
const result = verses.map(v => {
const mappingByLevel = Math.max.apply(Math, [...new Set(v.verses.map(w => w.level))])
const mappingByVerse = v.index
return {
level: mappingByLevel,
verse: mappingByVerse
}
}).filter(v => v.level === level).map(v => v.verse)
return result
}
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: getVerseIndexByLevel(i)
})
}
Is my code above consuming too much performance?
Could you make it cleaner and simpler without changing the result at all?
-I don't know what details I have to add more, StackOverflow forced me to write this because the post is mostly code. but I hope you understand just by looking at the results of the code.-
On the performance end, the one thing I can see that could improve it would be to call getVerseIndexByLevel only once, and use the calculated result mapping, instead of calling it multiple times (requiring re-parsing the input every time). Something like
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}
Another slight improvement - since you're using ES6 syntax, you don't need to .apply to Math.max - just spread the set into it.
You also don't need to turn the level numbers into a Set first - since it's being passed into Math.max anyway, repetitions aren't an issue.
const verses=[{index:1,verses:[{level:1},{level:1}]},{index:2,verses:[{level:1},{level:1}]},{index:3,verses:[{level:2}]},{index:4,verses:[{level:2}]},{index:5,verses:[{level:2},{level:2}]},{index:6,verses:[{level:3},{level:3}]},{index:7,verses:[{level:3},{level:3},{level:4},{level:4}]}];
function getVerseIndicies() {
const verseIndicies = {};
for (const v of verses) {
const level = Math.max(...v.verses.map(w => w.level));
verseIndicies[level] ??= [];
verseIndicies[level].push(v.index);
}
return verseIndicies;
}
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}

Javascript - How to make all object items set with different values?

NOTE: This is the first question that I ever created in StackOverflow.
I was trying to implement layer manager in javascript with this value:
var layers = [
{
id: "layer_1",
name: "Layer 1",
child: [
{
id: "layer_1A",
name: "Layer 1A",
child: [
{
id: "layer_1A1",
name: "Layer 1A1"
},
{
id: "layer_1A2",
name: "Layer 1A2"
}
]
},
{
id: "layer_1B",
name: "Layer 1B"
}
]
},
{
id: "layer_2",
name: "Layer 2"
},
{
id: "layer_3",
name: "Layer 3"
}
]
and save the result into this:
var indexSelector = {};
here is the code:
var value = [-1];
function read_layers(obj) {
// Checks the array (layer/child)
if (Array.isArray(obj)) {
for (let layer of obj) {
read_layers(layer);
}
}
// Checks the object
else if (typeof obj == "object") {
for (let layer of Object.entries(obj)) {
// Checks the childs
if (layer[0] == "layers" || layer[0] == "child") {
value.push(-1);
read_layers(obj[layer[0]]);
value.pop()
}
// Checks the object keys
else if (layer[0] == "id") {
if (!indexSelector.hasOwnProperty(layer[1])) {
++value[value.length-1];
indexSelector[layer[1]] = value; // Result
}
}
}
}
}
read_layers(layers);
I want the expected result to looks like this:
{
layer_1: [ 0 ]
layer_1A: [ 0, 0 ]
layer_1A1: [ 0, 0, 0 ]
layer_1A2: [ 0, 0, 1 ]
layer_1B: [ 0, 1 ]
layer_2: [ 1 ]
layer_3: [ 2 ]
}
But here is the problem result:
{
layer_1: [ 2 ]
layer_1A: [ 2 ]
layer_1A1: [ 2 ]
layer_1A2: [ 2 ]
layer_1B: [ 2 ]
layer_2: [ 2 ]
layer_3: [ 2 ]
}
How to fix this problem with different object values? Thanks.
NOTE: All assignment (with operator = ) of objects is applied with theirs reference, in javascript, like in as java.
So, at line indexSelector[layer[1]] = value; // Result , the value is assigned with its reference, not the whole value.
That's why the values of the result are all same.
Solution: Use indexSelector[layer[1]] = Object.assign({}, value);.
It'll solve your problem.

Lodash - DifferenceBy with different identity

I have the following array:
[
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
]
Every 5 seconds my application receives a new array and I need to compare the difference between the next one...
So the next array is:
[
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
]
Considering that identity is different. How can I compare with the previous and get an array with the excluded item?
[
{
id: 3
}
]
Use _.differenceWith():
const prev = [{"id":1},{"id":2},{"id":3},{"id":4}]
const next = [{"conversation_id":1},{"conversation_id":2},{"conversation_id":4}]
const diff = _.differenceWith(prev, next, ({ id }, { conversation_id }) => _.eq(id, conversation_id))
console.log(diff)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
I think you can use mix of javascript and lodash to solve this problem.
var arrayList = [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
var conv_array = [
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
var itemsNotInArray = [];
arrayList.filter(function (item) {
if (!_.find(conv_array, {conversation_id: item.id })) {
console.log("not in array", item);
itemsNotInArray.push(item);
}
});
console.log("result you expected", itemsNotInArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Filter the first array and compare each value till you find a missing id :
var array1 = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
var array2 = [{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
var test = array1.filter(
conv => !array2.find(
id => id.conversation_id === conv.id
)
);
console.log(test)
From lodash documentation, the third argument to differenceBy is
[iteratee=_.identity] (Function): The iteratee invoked per element.
Based on this, you can use
var current = [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
and
var next = [
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
then
var difference = _.differenceBy(current, next, function(obj) {
return obj.id || obj.conversation_id;
});
Or shortened with an arrow function:
var difference = _.differenceBy(current, next, (x) => x.id || x.conversation_id)

How to get one json object format from another JSON format

Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)

Cartesian product without duplicates

I am using a cartesian product function that given [1], [1,2,3], [1,2,3] returns 9 combinations:
[ [ 1, 1, 1 ],
[ 1, 2, 1 ],
[ 1, 3, 1 ],
[ 1, 1, 2 ],
[ 1, 2, 2 ],
[ 1, 3, 2 ],
[ 1, 1, 3 ],
[ 1, 2, 3 ],
[ 1, 3, 3 ] ]
But I need to remove those with the same items regardless of the order, so [ 1, 3, 1 ] and [ 1, 1, 3 ] are the same to me. The result should contain 6 items:
[ [ 1, 1, 1 ],
[ 1, 2, 1 ],
[ 1, 3, 1 ],
[ 1, 2, 2 ],
[ 1, 3, 2 ],
[ 1, 3, 3 ] ]
I can write a function that compares all possible pairs with _.xor, but for larger numbers it will probably be very inefficient. Is there a good way in Javascript to do this? An efficient way to compare all possible pairs or an algorithm for cartesian product without duplicates?
sort each array of the cartesian product
[ 1, 2, 1 ] -> [1 , 1 , 2]
[ 1, 1, 2 ] -> [1 , 1 , 2]
then gather these sorted arrays into a set, that will remove the duplicates.
Of course, you can do that while constructing the cartesian product rather than afterward.
JavaScript has Set and Map, however they compare objects and arrays by reference rather than by value, so you cannot take advantage of it directly. The idea is to use a key function which sorts and json encodes the items before putting it in a set.
pure ES5:
function product(sets) {
if (sets.length > 0) {
var head = sets[0];
var tail = product(sets.slice(1));
var result = [];
head.forEach(function(x) {
tail.forEach(function(xs) {
var item = xs.slice(0);
item.unshift(x);
result.push(item);
});
});
return result;
} else {
return [[]];
}
}
function myKeyFn(item) {
return JSON.stringify(item.slice(0).sort());
}
function uniqBy(items, keyFn) {
var hasOwn = Object.prototype.hasOwnProperty, keyset = {};
return items.filter(function(item) {
var key = keyFn(item);
if (hasOwn.call(keyset, key)) {
return false;
} else {
keyset[key] = 1;
return true;
}
});
}
function uniqProduct(sets) {
return uniqBy(product(sets), myKeyFn);
}
function log(x) {
console.log(x);
var pre = document.createElement('pre');
pre.appendChild(document.createTextNode(x));
document.body.appendChild(pre);
}
log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\n"));
<pre></pre>
lodash + modern JavaScript:
// Note: This doesn't compile on current babel.io/repl due to a bug
function product(sets) {
if (sets.length > 0) {
const [x, ...xs] = sets;
const products = product(xs);
return _.flatMap(x, head => products.map(tail => [head, ...tail]));
} else {
return [[]];
}
}
function uniqProduct(sets) {
return _.uniqBy(product(sets), x => JSON.stringify(x.slice(0).sort()));
}
console.log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\n"));
JavaScript has set data structure.
So store your results in a set where each element of the set is a collection of pairs of numbers from the original sets along with the number of times that number occurs.
So your result would look something like this:
[
{1:3},
{1:2, 2: 1},
{ 1:2, 3:1},
{ 1:1, 2:2},
{ 1:1, 2:1, 3:1},
{ 1:1, 3:2 } ]
This way, you won't be able to add the object a second time to the set.

Categories

Resources