Handsontable Sequence word creation - javascript

Thanks for this wonderful plugin.
I am using this plugin for grid functionality. For this I am trying to use sequence word creation on drag down.
I have implemented upto my knowledge. But I am facing one problem while select and drag multiple column.
I have created jsfiddle for this sample
var myData = [
["WIRE-001", 10, 11, 12, 13],
["WIRE-002", 20, 11, 14, 13],
["WIRE-003", 30, 15, 12, 13]
];
$("#exampleGrid").handsontable({
data: myData,
startRows: 5,
startCols: 5,
//minSpareCols: 1, //always keep at least 1 spare row at the right
minSpareRows: 10, //always keep at least 1 spare row at the bottom,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
currentRowClassName: 'currentRow',
currentColClassName: 'currentCol',
outsideClickDeselects: false,
fillHandle: true,
beforeAutofill: function(start, end, data) {
console.log(arguments);
console.log(start);
console.log(end);
console.log(data);
var selectedVal = this.getSelected();
var selectedData = this.getData(selectedVal[0], selectedVal[1], selectedVal[2], selectedVal[3]);
var sequenceNum = [];
var sequenceWord = [];
var numberFormat = 1;
if (start.col == 0) {
for (var j = 0; j < selectedData.length; j++) {
var numbers = selectedData[j][0].match(/[0-9]+$/g);
if (numbers && !isNaN(numbers[0])) {
numberFormat = numbers[0].length;
sequenceNum.push(Number(numbers[0]));
}
var words = selectedData[j][0].match(/[A-Za-z\-]+/g);
if (words && isNaN(words[0])) {
sequenceWord.push(words[0]);
}
}
var prefix = sequenceWord.length > 0 ? sequenceWord[0] : "";
var lastValue = sequenceNum[sequenceNum.length - 1]
var diff = sequenceNum.length > 1 ? (sequenceNum[sequenceNum.length - 1] - sequenceNum[sequenceNum.length - 2]) : 1;
for (var i = 0; i < end.row; i++) {
if (!data[i]) { data[i] = []; }
data[i][0] = prefix + pad((lastValue + diff), numberFormat);
diff++;
}
}
},
afterChange: function(changes, source) {
}
});
Appreciate for helping to solve this.

I recommend opening the console and checking the errors since it becomes very clear what your problem is if you do so. The example that leads to the bug that you mentioned on your comment uses an empty row. In your code you are doing
var words = selectedData[j][0].match(/[A-Za-z\-]+/g);
However, selectedData[j][0] will be null. I would simply add a check for empty rows and handle it appropriately (toss it or default to some value)

Related

Add constraint to Excel column using XLSX/SheetJS

I'm trying to add a constraint to one of my columns which would prevent entering values in the column C that are lesser than those in the column B.
For example, if the value in B1 is 10, inputting 7 in the column C1 shouldn't be allowed.
If the value in B2 is 4, inputting anything less than 4 in the column C1 shouldn't be allowed, etc.
So far I managed to produce a simple file where each cell in the third column is a sum of first and second column's cells - C1=A1+B1, C2=A2+B2... (Notice that column A is used only for the sake of this example). downloadSheet() function is where the the logic is implemented.
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>
<button onclick="downloadSheet()">Download excel</button>
<script>
function uigrid_to_sheet(data, columns) {
var o = [],
oo = [],
i = 0,
j = 0;
/* column headers */
for (j = 0; j < columns.length; ++j) oo.push((columns[j]));
o.push(oo);
/* table data */
for (i = 0; i < data.length; ++i) {
oo = [];
for (j = 0; j < data[i].length; ++j) oo.push((data[i][j]));
o.push(oo);
}
/* aoa_to_sheet converts an array of arrays into a worksheet object */
return XLSX.utils.aoa_to_sheet(o);
}
var s2ab = function (s) {
var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
var view = new Uint8Array(buf); //create uint8array as viewer
for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
return buf;
}
function downloadSheet() {
var sheetName = 'first_sheet';
var wopts = { bookType: 'xlsx', bookSST: true, type: 'binary' };
var fileName = "the_excel_file.xlsx";
var columns = ['first', 'second', 'result'];
var data = [
[1, 20],
[2, 32],
[3, 18],
[4, 11]
];
var wb = XLSX.utils.book_new();
var ws = uigrid_to_sheet(data, columns);
ws['!ref'] = XLSX.utils.encode_range({
s: { c: 0, r: 0 },
e: { c: 2, r: 0 + data.length }
});
data.forEach((element, i) => {
ws['C'+(i+2)] = { f: 'A'+(i+2) + '+' + 'B'+(i+2) };
});
XLSX.utils.book_append_sheet(wb, ws, sheetName);
var wbout = XLSX.write(wb, wopts);
saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), fileName);
}
</script>
Logically and functionally what I require is different, but I can't seem to figure what formula (or other method) should I use. I tried simply using formula
ws['C1'] = { f: 'C1>=B1' };
but this simpleton attempt unsurprisingly failed with a message that it caused a circular reference.
This requirement was made because our systems allow updating our web shop's prices through an excel file which can be manually edited and sent to server. The problem is that guys in that department can sometimes accidentally omit or add a cypher and then we get ourselves incorrect prices on the web shop. Column B in my example is the purchase price, column C is the Web Shop price, and values in column C should never be allowed to get under that purchase price in column B.
Edited to include entire HTML so someone can just copy paste it into a new HTML file and it should work.

How do I iterate through an object containing 2D arrays without getting O(n)^3?

Hopefully this is not too specific of a question, but I'm hoping to get insight into how to work with nested Objects and Arrays without making a program that runs O(n)^2 or O(n)^3.
Below is a program I built to find the cheapest flight(s) from point A to point B, and if the flight is the same price, the one with the least amount of flights. You will see where I label the parts that are not efficient. How can I make these more efficient?
//returns an object with an array of IDs that will get you to a destination at the lowest cost. if same cost, then the least amount of flights flights
function leastExpensiveFlight(flightInfo, start, end) {
var numOfTimes = 1
var combinedValues = []
var minValue
var indexOfMinValue;
var numOfFlights = []
var minNumberOfFlights;
var startTimes = flightInfo.reduce((startTimes, f, index) => {
if (f[0] == start) {
startTimes[numOfTimes] = [f];
numOfTimes++
}
return startTimes
}, {})
//below is O(n)^2
//Gets the Flights where Location 1 = start
for (i = 0; i < Object.keys(startTimes).length; i++) {
for (j = 0; j < startTimes[Object.keys(startTimes)[i]].length; j++) {
flightInfo.forEach((f, ) => {
if (startTimes[Object.keys(startTimes)[i]][j] &&
f[0] === startTimes[Object.keys(startTimes)[i]][j][1] &&
startTimes[Object.keys(startTimes)[i]][j][1] <= end &&
f[1] <= end) {
if (!startTimes[Object.keys(startTimes)[i]].includes(f)) {
startTimes[Object.keys(startTimes)[i]].push(f)
}
}
})
}
}
//below is O(n)^3!!
//Finds trips that will get to the destination
Object.keys(startTimes).forEach((flights) => {
startTimes[flights].forEach((subFlights1, sFIndex1) => {
startTimes[flights].forEach((subFlights2, sFIndex2) => {
if (subFlights1[0] === subFlights2[0] &&
subFlights1[1] === subFlights2[1] &&
sFIndex1 != sFIndex2) {
if (subFlights1[2] >= subFlights2[2]) {
startTimes[flights].splice(sFIndex1, 1)
} else {
startTimes[flights].splice(sFIndex2, 1)
}
}
})
})
})
//below is O(n)^2
//Finds the trip with the minimum value and, if same price, least amount of flights
Object.keys(startTimes).forEach((flights, sTIndex) => {
startTimes[flights].forEach((subFlights, sFIndex) => {
if (sFIndex == 0) {
combinedValues[sTIndex] = subFlights[2]
numOfFlights.push(1)
} else {
combinedValues[sTIndex] += subFlights[2]
numOfFlights[sTIndex] += 1
}
if (sFIndex === startTimes[flights].length - 1 &&
((combinedValues[sTIndex] <= minValue) ||
!minValue)) {
if ((!minNumberOfFlights ||
minNumberOfFlights > numOfFlights[sTIndex])) {
minNumberOfFlights = numOfFlights[sTIndex]
}
if (combinedValues[sTIndex] === minValue &&
numOfFlights[sTIndex] === minNumberOfFlights ||
(combinedValues[sTIndex] < minValue &&
numOfFlights[sTIndex] >= minNumberOfFlights) ||
!minValue) {
minValue = combinedValues[sTIndex];
indexOfMinValue = sTIndex
}
}
})
})
return startTimes[Object.keys(startTimes)[indexOfMinValue]]
} //Big O is O(n)^3
//2D Array: data[0][0] = Location start,
// data[0][1] = Location end,
// data[0][2] = Price,
//
var data = [
[4, 5, 400],
[1, 2, 500],
[2, 3, 300],
[1, 3, 900],
[2, 3, 100],
[3, 4, 400]
]
//so from location 1 - location 3, the cheapest route would be: [1,2,500],[2,3,100]
console.log(JSON.stringify(leastExpensiveFlight(data, 1, 3)));
Your help is much appreciated. I am trying to get better at coding so I can go into my engineering department, but as you can see, I have a long way to go.
[EDIT]- Sorry, had an erroneous statement in there that I just fixed.
I'd approach this by first constructing a graph for each possible path between the start and end nodes. Make a function that, given a start node and a list of nodes already visited, can look through the data to find each path leading out of this node. For each path which has not been visited yet, if the path ends at the destination, push the whole result of the paths traveled to a finishedPaths array. Otherwise, recursively call the function so that it does the same process again, starting at the end node.
To reduce complexity, to identify whether a node has or hasn't been visited yet, use an object or Set (which have lookups in O(1) time, unlike Array.prototype.includes, which runs in O(n) time).
At the end, look through all the possible paths between the start and end, identify the ones with the lowest cost, and among those, find the one with the least number of paths.
function leastExpensiveFlight(data, start, end) {
const pathsByStartLoc = {};
for (const path of data) {
const start = path[0];
if (!pathsByStartLoc[start]) pathsByStartLoc[start] = [];
pathsByStartLoc[start].push(path);
}
const getAllPaths = (
start,
costSoFar = 0,
pathsSoFar = [],
finishedPaths = [],
visitedNodes = {}
) => {
if (!pathsByStartLoc[start]) return;
for (const path of pathsByStartLoc[start]) {
if (visitedNodes.hasOwnProperty(path[2])) continue;
if (path[1] === end) {
finishedPaths.push({ totalCost: costSoFar + path[2], paths: [...pathsSoFar, path] });
}
getAllPaths(
path[1],
costSoFar + path[2],
[...pathsSoFar, path],
finishedPaths, { ...visitedNodes, [path[0]]: true }
);
}
return finishedPaths;
};
const allPaths = getAllPaths(start);
const lowestCost = Math.min(...allPaths.map(({ totalCost }) => totalCost));
const lowestCostPaths = allPaths.filter(({ totalCost }) => totalCost === lowestCost);
return lowestCostPaths.reduce((a, item) => a.paths.length < item.paths.length ? a : item);
}
var data = [
[4, 5, 400],
[1, 2, 500],
[2, 3, 300],
[1, 3, 900],
[2, 3, 100],
[3, 4, 400]
]
//so from location 1 - location 3, the cheapest route would be: [1,2,500],[2,3,300]
console.log(leastExpensiveFlight(data, 1, 3));

Dynamic width for each column if value is 0 in highcharts

I'm new to highcharts plugin and I need to set different width for each column if the value is empty.
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
And i need to display it this way
I need this
I have been searching the documentation but i don't find anything that does it, without a lot of code.
I achieved this, but it doesn't upload/resize dynamically.
http://jsfiddle.net/5vgjztk0/
Thanks
You can use something like merimekko chart.
(function(H) {
var each = H.each;
var xStart = 0;
var rememberX = 0;
H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function(proceed) {
var series = this;
if(series.data.length > 0 ){
each(this.data, function(point) {
point.shapeArgs.width = series.xAxis.translate(point.x) - series.xAxis.translate(xStart);
point.shapeArgs.x = series.xAxis.translate(point.x) - point.shapeArgs.width;
xStart = point.x;
});
xStart = 0;
}
proceed.call(this);
})
})(Highcharts)
Demo:
http://jsfiddle.net/js55fuo1/24

How can I style highchart select to look like hover?

Highcharts is an amazing library, however I need to programatically scroll through the chart as if the cursor itself was hovering across the x-axis. The only way I can think of doing this is by selecting points, unfortunately I could not find options in highcharts api documentation that allows me to put crosshairs/ playheader on selected points.
How can I do this?
This is not part of the API, but you can use point.onMouseOver() to fire mouseover event: http://jsfiddle.net/15dzo23n/1/
Example:
var index = 0;
$('#container').highcharts({
chart: {
events: {
load: hoverAnim
}
},
tooltip: {
crosshairs: true
},
series: [{
name: 'AAPL',
data: [5, 10, 15, 12, 13, 14, 12, 1, 12, 1, 12, 12, 12, 1]
}]
});
function hoverAnim(e) {
var chart = this,
points = chart.series[0].points,
len = points.length;
if (index < len) {
chart.hoverSeries = chart.series[0];
chart.hoverPoint = points[index];
chart.pointer.runPointActions(e); //display crosshair
points[index].onMouseOver(e); // display tooltip and set hover state
index++;
setTimeout(function () {
hoverAnim.call(chart, e);
}, 100);
}
}
Pawel Fus had a good answer, however I found an even better solution which doesn't have the buggy lag behavior when switching to different time points. Also events are not needed in this solution.
var chart = myElement.highcharts();
function select(value) {
// Scroll to some time.
var axes = chart.axes;
for (var i = 0; i < axes.length; i ++) {
var axis = axes[i];
axis.drawCrosshair(null, chart.series[0].points[value])
}
for (i = 0; i < chart.series.length; i++) {
var series = chart.series[i];
// Unset last value
series.points[oldValue].setState('');
// Set new value
series.points[value].setState('hover');
}
oldValue = value;
});

Removing item from array based on id

I have two array's. One of id's of elements that I need to show and then another of the elements in a drop down. If the id's match then then show. It sounds simple, but I'm having trouble slicing out the elements that do not need to show.
So it kind of looks like this
var option = //Ton of objects with values that match with the id that look like this, there are about 70 of them. I need to loop through these to get the value of each one. Please see screen shot, I don't know how else to show that they are objects.
var ids = [16, 15, 17, 18, 5];
and then I was trying to loop through each option and see if the value is equal to the Id. For some reason this is removing everything instead of just the once that need to be removed.
for(var i=0 ; i<option.length; i++){
if (option[i].value !== ids) {
option.splice(i);
}
}
Try out the filter function:
var newOptions = options.filter(function(option, index) {
return ids.indexOf(option.value) >= 0;
});
var values = newOptions.map(function(option) {
return option.value;
});
If you want to remove items from the option array:
var option = [
{display:'opt50', value:50 },
{display:'opt16', value:16 },
{display:'opt10', value:10 },
{display:'opt17', value:17 },
{display:'opt43', value:43 },
{display:'opt5', value:5 }
]
var ids = [16, 15, 17, 18, 5];
for(var i = option.length - 1; i > -1; i--) {
if(ids.indexOf(option[i].value) == -1) {
option.splice(i,1);
}
}
// logs [{"display":"opt16","value":16},{"display":"opt17","value":17},{"display":"opt5","value":5}]
console.dir(JSON.stringify(option));
If you want to just find matching items and keep option intact:
// fill in options with some elements
var option = [
{display:'opt50', value:50 },
{display:'opt16', value:16 },
{display:'opt10', value:10 },
{display:'opt17', value:17 },
{display:'opt43', value:43 },
{display:'opt5', value:5 }
]
var ids = [16, 15, 17, 18, 5];
var filteredOption = option.filter(function(o) {
return ids.indexOf(o.value) > -1;
});
// logs [{"display":"opt16","value":16},{"display":"opt17","value":17},{"display":"opt5","value":5}]
console.dir(JSON.stringify(filteredOption));
The !== operator is always going to return true.
12 !== [12,34,45]
true
13 !== [12,34,45]
true
As a result, if (option[i].value !== ids) { will always execute option.splice(i); since the compared values are always different types.
Mozilla's Sameness documentation has a great explanation and useful chart.
var option = [{display: "Member Form",value: 2}, {display: "Member Form",value: 5}];
var ids = [16, 15, 17, 18, 5];
var optionKey = Object.keys(option);
for(var i = optionKey.length - 1; i >= 0 ; i--){
if (ids.indexOf(option[optionKey[i]].value) == -1) {
option.splice(i, 1) //second argument allows splice to only delete one element.
}
}
console.log(option);
document.write(JSON.stringify(option)); //result!

Categories