I am writing a component in Vue that lets us take an array of objects for a table and apply HTML formatting to the data, but the way I have it written currently is an O(n^2) complexity, and I am not sure how to make it any faster.
example row data:
[
{
"product": "Socks",
"price": 39,
"sales": 20,
"inventory": 68
},
{
"product": "Shoes",
"price": 23,
"sales": 99,
"inventory": 79
},
{
"product": "Pants",
"price": 45,
"sales": 46,
"inventory": 58
}
]
example formatter array:
[
{
data: 'product',
format: (data) => {
return '<em>' + data + '</em>';
}
},
{
data: 'price',
format: (data) => {
return '$' + data + '.00';
}
},
{
data: 'sales',
format: (data) => {
return '<span class="text-success">' + data + '</span>';
}
},
{
data: 'inventory',
format: (data) => {
return '<span class="text-primary">' + data + '</span>';
}
},
];
And here is the code that takes the formatter and applies it to the data:
for (let row of rows) {
let renderedRow = {};
for (let key in row) {
let format = render.find(renderObject => renderObject.data === key);
renderedRow[key] = format.format(row[key]);
}
renderedRows.push(renderedRow);
}
this.rows = renderedRows;
The loop iterates over the rows and then iterates over the object in the row. Inside the object's loop, it finds the formatter (renderObject) with the matching key where data === key, and then uses the renderObject's format function to apply the formatting and push the data to a new array.
Because the formatting needs to be applied to the data in every row, I tried to just access it using the key, but since each key is inside an object inside an array I couldn't figure out how to access that.
A simple thing you could do is take the find call and change it to a look-up table computed before the nested loop starts:
const formatters = Object.fromEntries(render.map(({ data, format }) => [data, format]));
for (let row of rows) {
let renderedRow = {};
for (let key in row) {
let format = formatters[key];
renderedRow[key] = format.format(row[key]);
}
renderedRows.push(renderedRow);
}
this.rows = renderedRows;
Related
I am really struggling with the Object transformation. I have an array of Object and want to transform it into Highcharts multi line chart input. I want to get the unique dates first sorted from low to high, which will go into x axis and then transform the Original based on ID and date. The length for each ID.data is going to remain same (for whatever date count is not available for that date it will come as null)
Original:
[
{
"date": "1997-09-29",
"Count": 100,
"ID": "AB12-R"
},
{
"date": "1997-12-30",
"Count": 104.7,
"ID": "AB13-R"
},
{
"date": "1998-03-30",
"Count": 981,
"ID": "BA12-R"
},
{
"date": "1998-06-01",
"Count": 341,
"ID": "BA12-R"
}
]
Transformed:
[{
Identiy : 'AB12-R',
data : [100,null,null,null]
},
{
Identiy : 'AB13-R',
data : [null,104.7,null,null]
},{
Identiy : 'BA12-R',
data : [null,null,981,341]
}]
I have tried with reduce but nothing is working it seems. I am able to group it by ID but not able to handle the null and missing count, Can someone please help me here ?
This is what i have tried:
const result = Original.reduce(function (r, a) {
r[a.ID] = r[a.ID] || [];
r[a.ID].push(a);
return r;
}, Object.create(null));
console.log({'Transformed':result})
Take a look at this solution:
const hcData = [];
data.forEach((d, i) => {
const checkIfExist = hcData.find(data => data.id === d["ID"])
if (checkIfExist) {
checkIfExist.data[i] = d["Count"];
} else {
const initialData = [...Array(data.length)]
initialData.fill(null, 0, data.length)
initialData[i] = d["Count"];
hcData.push({
data: initialData,
id: d["ID"]
})
}
})
Demo: https://jsfiddle.net/BlackLabel/k2dg1wns/
I have an array that contains multiple objects and each object has an array called data that contains multiple data.
"datasets": [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
},
...
]
For each data array that I have, how can I write a logic so that I store the values for each matching index into a new array data set. For example, I need to generate a new array which would only contains the values at index zero like so:
[1227.0, 302.0, 239.0]
and then another array which would contain the values at index one only
[698.4, 170.7, 275.5]
The desired output that I need is the following:
"result": [
{
data: [1227.0, 302.0, 239.0]
},
{
data: [698.4, 170.7, 275.5]
},
{
data: [2903.1, 592.2, 353.5]
},
{
data: [7280.2, 1293.6, 478.0]
},
{
data: [5447.9, 961.3, 576.9]
}
]
How will I achieve this. Can someone please help me out?
It looks like you need to transpose your data. Here's one possible solution:
Assuming this was nested inside an object called dataset:
Ex:
const dataSet = {
"datasets": [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
},
...
]
}
Now, this is a bit of a cumbersome process, but the solution would involve:
Iterating through every element object of dataSet["datasets"]
Create a new array upon every increment of i
Stopping at the jth element of dataSet["datasets"][i].data[j] and store that it in the array instance
When you've went through every object element's jth position, push that array instance into an output array.
Here's how one solution would look (O(n^2)) :
const matrixObjTranspose = (matrixObj) => {
const output = [];
for (let i = 0; i < matrixObj.datasets[0].data.length; i += 1) {
const newSubArr = [];
for (let j = 0; j < matrixObj.datasets.length; j += 1) {
newSubArr.push(matrixObj.datasets[j].data[i]);
}
output.push(newSubArr);
}
return output;
};
console.log(matrixObjTranspose(dataSet))
const c = [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
}]
const convert = (arr) => {
return arr.slice(1).reduce((sum, cur) => {
return sum.map((v, i) => (Array.isArray(v) ? v : [v]).concat(cur.data[i]))
}, arr[0].data).map(v => ({data: v}))
}
console.log(convert(c))
I have an array of objects:
var items = [
{
"id":"sugar",
"type": 'eatables'
},
{
"id":"petrol",
"type": 'utility'
},
{
"id":"apple",
"type": 'fruits'
},
{
"id":"mango",
"type": 'fruits'
},
{
"id":"book",
"type": 'education'
}
];
Now I have another array of orders with the help of which I want to sort items array:
var orders = [
{
"id":"sugar",
"order":5
},
{
"id":"book",
"order":1
}
];
Now what I am trying so far in my logic is that I am putting so many loops that it is totally creating mess.
Can anyone suggest me with a short and optimized logic for this?
One approach could be creating one dictionary which will keep the order for every element. Also, I've iterated the whole items array to store the position for the elements that are not in the orders array.
First of all, I'll declare one array which keep the whole orders, thus one array with 1..N elements.
var orderNumbers = Array.from({length: items.length}, (_, v) => v + 1);
Then I started to create the dictionary by iterating orders array and remove orders from orderNumbers.
The last step is iterating the items array and use shift method to "pop" the first element.
The final dictionary will look like
{
"sugar": 2,
"book": 3,
"petrol": 1,
"apple": 4,
"mango": 5
}
In this code I used one dictionary because its lookup has complexity of O(1).
var items = [ { "id":"sugar", "type": 'eatables' }, { "id":"petrol", "type": 'utility' }, { "id":"apple", "type": 'fruits' }, { "id":"mango", "type": 'fruits' }, { "id":"book", "type": 'education' } ], orders = [ { "id":"sugar", "order":2 }, { "id":"book", "order":3 } ], orderNumbers = Array.from({length: items.length}, (_, v) => v + 1);
var ordersDict = orders.reduce((acc, item) => {
acc[item.id] = item.order;
//remove from order numbers
let index = orderNumbers.findIndex(el => el == item.order);
orderNumbers.splice(index, 1);
return acc;
}, {});
for(let i = 0; i < items.length; i++){
if(!ordersDict.hasOwnProperty(items[i].id)){
ordersDict[items[i].id] = orderNumbers[0];
orderNumbers.shift();
}
}
//sort the array
items.sort((a,b) => ordersDict[a.id] - ordersDict[b.id]);
console.log(items);
let oorder = new Object();
orders.map(item=>{oorder[item.id]=item.order});
var new_items = [];
items.map(item=>{new_items[oorder[item.id]-1]=item});
I am using angular-highcharts for my project.I am used https://www.highcharts.com/demo/column-basic this chart for my data.I have below data format.
[
{
"project": "train project1",
"hours": {
"AD": 58265
}
},
{
"project": "butify",
"hours": {
"AD": 3940
}
},
{
"project": "skler",
"hours": {
"AD": 563250
}
},
{
"project": "Internal Application",
"hours": {
"AD": 33325,
"DAM": 328095
}
},
{
"project": "train2project",
"hours": {
"AD": 137215
}
},
{
"project": "CELLProje1",
"hours": {
"DAM": 488470
}
},
{
"project": "rangeselector",
"hours": {
"AD": 3015,
"DAM": 71175
}
},
{
"project": "Android dev",
"hours": {
"AD": 99160
}
},
{
"project": "Web Application",
"hours": {
"AD": 72720
}
}
];
The values inside "hours" will be one or more.I have added my fiddle tried so far.I struggling to form json for series data.Also I need form X -axis for the graph that should be in an array.
Ex:
categories: [
'train project1',
'beautify',
'skler',
'Internal Application',
'train project2',
'rangeselector',
'Android',
'Web Application'
],
X-axis formation will be right?
http://jsfiddle.net/harifrais/uxpvs8fw/34/
You're trying to get data into this format
[
{
name:"series-name",
data:[ ... ]
}
]
But to use categories, there must be the same number of elements in each series as there are categories. And as every hours element in your input data does not contain all the same you need to do a little more work.
Get a distinct list of all the keys from hours
Loop over every element and use project as a category
Fill in zeros where any element does not occur in hours
So you can do this in a fairly simple 2-step process using reduce and map.
var data = [{"project":"train project1","hours":{"AD":58265}},{"project":"butify","hours":{"AD":3940}},{"project":"skler","hours":{"AD":563250}},{"project":"Internal Application","hours":{"AD":33325,"DAM":328095}},{"project":"train2project","hours":{"AD":137215}},{"project":"CELLProje1","hours":{"DAM":488470}},{"project":"rangeselector","hours":{"AD":3015,"DAM":71175}},{"project":"Android dev","hours":{"AD":99160}},{"project":"Web Application","hours":{"AD":72720}}];
// get a distinct list of hour keys
var seriesData = data.reduce( (acc, {hours}) => {
Object.keys(hours).forEach(key => {
if(!acc[key]) acc[key] = [];
})
return acc;
},{});
// reduce the original data to get categories and series values
// filling in zeros where necessary
var result = data.reduce( (acc, {project,hours}) => {
acc.categories.push(project);
Object.keys(acc.seriesData).forEach(s => {
acc.seriesData[s].push(hours[s] || 0);
});
return acc;
},{categories:[],seriesData:seriesData});
// shape the data to how highcharts wants it
var categories = result.categories;
var series = Object.entries(result.seriesData).map( e => ({
name: e[0],
data:e[1]
}));
console.log(categories);
console.log(series);
Here's an updated fiddle to show highcharts/your data: https://jsfiddle.net/u7opL2dw/2/
Here is my idea how to parse your data to using in the Highcharts library, no matter how many properties will be in the data.hours object.
Demo: http://jsfiddle.net/BlackLabel/31tp0mkw/
const categories = sampleJson.map(data => data.project);
const getSeriesNames = sampleJson.map(data => {
for (let i in data.hours) {
return i
}
}).filter((item, i, ar) => ar.indexOf(item) === i);
const series = getSeriesNames.map(name => {
let output = {
name: name,
data: []
};
sampleJson.forEach(data => {
if (data.hours[name]) {
output.data.push(data.hours[name])
} else {
output.data.push(null)
}
});
return output
})
Need to format my array of objects to have a nested object.
Tried using map, and reduce. Can't seem to get the formatting correct.
Currently getting this.
[
{
"date": "2019-10-01T00:00:00Z",
"total": 20
}
]
Need it to be in this format.
[
{
name: 'Total Call Volume',
data: {
'2019-01-01 00:00:00 -0800': 0,
'2019-01-01 00:00:00 -0800': 88,
}
}
]
var arr = this.$store.state.aggregatedCallVolumeData;
var mapData = arr.map(item => ({
date: item.startTime,
total: item.dataValues.TOTAL_CALL_VOLUME
}));
var data = mapData.reduce((acc, value) => {
acc[value.date] = acc[value.date] ? acc[value.date] : [];
acc[value.date] ? acc[value.date].push(value.total) : [value.total];
return acc;
}, {});
let result = Object.entries(data).map(d => ({ id: d[0] + ':' + d[1] }) );
console.log(result)
console output is
0: {id: "2019-10-27T00:00:00Z:0"}
1: {id: "2019-10-28T00:00:00Z:88"}
arr.reduce((obj, item) => {
obj.data[item.date] = item.total;
return obj;
}, {
name: 'Total Call Volume',
data: {}
})
something like that. You might need to switch around the 'obj' and 'item' param. Basically the obj param is the second argument in the reduce function. So the obj is that base object I added at the end, and the 'item' param is each item in the array. So we loop and keep adding the values on to the 'obj' and return it for the next loop to add onto