I have been given a dataset that I have identified contains records with a numeric overflow eg -214012688. Is there a way for me to fix this?
Here's a sample of data I have
2142936357
2143000225
2142936544
2142974737
2142935734
2143051458
-2143303677
-2142887448
2142866111
2142864212
2143020049
2143009445
2143300604
2143121125
-2142802104
2142801451
The dataset is not sorted so the numbers aren't in order. when I asked my client why there's negative numbers its because it went over the max integer amount.
If any of your values have become positive after rolling over, there is no way to fix it, but if that's not the case, you can write a custom function to get the absolute value of the difference between each negative value and the signed minimum, then add that to the signed maximum. Here is a JavaScript function for it:
function signedRolloverToUnsigned (x, signedMin, signedMax) {
if (x < 0) {
x = Math.abs((signedMin - 1) - x) + signedMax;
}
return x;
}
And here is the custom Google sheet function you'd need if you just want to copy + paste and use it in your document:
/**
* Converts signed rollover (negative) values to their equivalent unsigned positive values
*
* #param {number} value The value to convert.
* #return The unsigned representation of the input value.
* #customfunction
*/
function signedRolloverToUnsigned (value) {
if (value < 0) {
value = Math.abs((-2147483648 - 1) - value) + 2147483647;
}
return value;
}
Here is a working example using the data you provided:
'use strict';
function signedRolloverToUnsigned (x, signedMin, signedMax) {
if (x < 0) {
x = Math.abs((signedMin - 1) - x) + signedMax;
}
return x;
}
const text = `2142936357
2143000225
2142936544
2142974737
2142935734
2143051458
-2143303677
-2142887448
2142866111
2142864212
2143020049
2143009445
2143300604
2143121125
-2142802104
2142801451`;
const arrSource = text.split('\n').map((x) => {
return parseInt(x);
});
const signedRange = [-2147483648, 2147483647];
const arrFixed = arrSource.map((x) => signedRolloverToUnsigned(x, signedRange[0], signedRange[1]));
//---Ignore: For displaying output --->
document.body.style = 'background-color: #eeeeee; padding: 1rem;';
const pre = document.body.appendChild(document.createElement('pre'));
const samp = pre.appendChild(document.createElement('samp'));
samp.textContent = arrFixed.join('\n');
const styles = {
'box-sizing': 'border-box',
'background-color': 'white',
'border-radius': '0.5rem',
'display': 'block',
'padding': '1rem',
};
for (const [prop, value] of Object.entries(styles)) {
samp.style.setProperty(prop, value);
}
Related
The google slides API documentation says on shapes/text elements it can accept "AUTOFIT_TEXT" as an autofit type, however this simply doesn't work and the API throws an error saying "autofitType can only be set to NONE", see here
My alternate pathway solution here is to attempt to come up with a method that can recalculate or estimate the font-size needed for the width of the element in question.
How would I go about doing this?
The font size could be anything it doesn't matter, I just need the text I'm replacing in the element to NOT wrap.
Simply just duplicating the slide with the API resets all the auto fit options already set in the google slide template.
Let's say the string length is "Hi-dilly-ho, neighborinos!", which has a length of 26, and let's say the width of the element is say 200PX. I'd need to come up with a method to fit the text on one line!
There could simply be a magic number I come up with here and multiply that by the length of the string which may work, but i was wondering if there's a nicer way with nodejs that anyone can think of
As i couldn't find a good example of this anywhere, i wrote a method to re-calculate the font-size based on the dimensions of the input element from the Google Presentation.
This workaround calculates the size of the element (width and height) and using node canvas, i created a text element and measure the size of the text which works for multiline text as well.
This is NOT as smart as the default autofit inside gslides, as it bases the amount of chars that can fit of the W key as it's the widest key usually in font faces.
There's a few helpers i've created here which are used by the main function:
// An English Metric Unit (EMU) is defined as 1/360,000 of a centimeter and thus there are 914,400 EMUs per inch, and 12,700 EMUs per point.
export const convertEMUToPT = (emu: number): number => emu / 12700;
// convert pixles to PT, there is 0.75pt to a px
export const covertPXtoPT = (px: number): number => px * 0.75;
// convert PT to PX, there is 0.75pt to a px
export const convertPTtoPX = (px: number): number => px / 0.75;
// this is a very simple example of what i have, obviously you'll need error handling if those values don't exist
// The below will return the dimensions in EMU, to convert to PT divide the EMU value by `12700`
export function getElementSize(element: slides_v1.Schema$PageElement) {
const width = element?.size?.width?.magnitude * element.transform?.scaleX;
const height = element?.size?.height?.magnitude * element.transform?.scaleY;
return { width, height };
}
/**
* #name findByKey
* #description This was introduced as the fontWeight key for example could be on a mixture of elements, and we
* want to find them whereever they may be on the element so we can average out the values
* #function
* #param obj - any object to search
* #param kee - representing the needle to search
* #returns any - returns the value by the key if found
*/
export const findByKey = (obj: any, kee: string): any | undefined => {
if (kee in obj) {
return obj[kee];
}
for (const n of Object.values(obj).filter(Boolean).filter(v => typeof v === 'object')) {
const found = findByKey(n, kee);
if (typeof found !== 'undefined') {
return found;
}
}
};
/**
* #name splitter
* #description Based on the maximum allowed characters on a single line, we split the lines
* based on this value so we can calculate multi line text wrapping and adjust the font size
* continually within a while loop
* #function
* #param str - the input string
* #param l - the length of each "line" of text
* #returns string[] - an array of strings representing each new line of text
*/
export function splitter(str: string, l: number): string[] {
const strs = [];
while (str.length > l) {
let pos = str.substring(0, l).lastIndexOf(' ');
pos = pos <= 0 ? l : pos;
strs.push(str.substring(0, pos));
let i = str.indexOf(' ', pos) + 1;
if (i < pos || i > pos + l)
i = pos;
str = str.substring(i);
}
strs.push(str);
return strs;
}
import { createCanvas } from 'canvas';
export function calculateFontSize(element: slides_v1.Schema$PageElement, text: string): number {
// get the dimensions of the element
const size = getElementSize(element, false);
// create a canvas with the same size as the element, this most likely does not matter as we're only measureing a fake
// representation of the text with ctx.measureText
const canvas = createCanvas(convertPTtoPX(size.width), convertPTtoPX(size.height));
const ctx = canvas.getContext('2d');
// try to extract all the font-sizes
const fontSizes = element.shape?.text?.textElements?.map(textElement => textElement.textRun?.style?.fontSize?.magnitude).filter((a): a is number => isNumber(a)) ?? [];
// try to extract all the font-weights
const fontWeights = element.shape?.text?.textElements?.map(textElement => textElement.textRun?.style?.weightedFontFamily?.weight).filter((a): a is number => isNumber(a)) ?? [];
// fallback to arial if not found, if there's more than one fontFamily used in a single element, we just pick the first one, no way i can think of
// to be smart here and not really necessary to create multiple strings with different fonts and calculate those, this seems to work fine
const fontFamily = findByKey(element, 'fontFamily') ?? 'Arial';
// calulate the average as there can be different fonts with different weights within a single text element
const averageFontWeight = fontWeights.reduce((a, n) => a + n, 0) / fontWeights.length;
const averageFontSize = fontSizes.reduce((a, n) => a + n, 0) / fontSizes.length;
// if the average font-weight is not a number, usae the default
const fontWeight = isNaN(averageFontWeight) ? DEFAULT_FONT_WEIGHT : averageFontWeight;
// use the average fontSize if available, else start at an arbitrary default
let fontSize = isNaN(averageFontSize) ? DEFAULT_FONT_SIZE : averageFontSize;
// if the input value is an empty string, don't bother with any calculations
if (text.length === 0) {
return fontSize;
}
// create the initial font value, this is overwritten during the while loop
ctx.font = `${fontWeight} ${fontSize}pt ${fontFamily}`;
// max chars we will fit horizontally based on the char width of W
const getCharWidth = (): number => convertPTtoPX(size.width) / ctx.measureText('W').width;
// used for the while loop, to continually resize the shape and multiline text, until it fits within the bounds
// of the element
const isOutsideBounds = (): boolean => {
// based on the maximum amount of chars available in the horizontal axis for this font size
// we split onto a new line to get the width/height correctly
const multiLineString = splitter(text, getCharWidth()).join('\n');
// get the measurements of the current multiline string
const metrics = ctx.measureText(multiLineString);
// get the width in PT
const width = covertPXtoPT(metrics.width);
// the emAcent/Decent values do exist, it's the types that are wrong from canvas
// #ts-expect-error
const emAcent = metrics.emHeightAscent as number;
// #ts-expect-error
const emDecent = metrics.emHeightDescent as number;
const height = covertPXtoPT(emAcent + emDecent);
return width > size.width || height > size.height;
};
// continually loop over until the size of the text element is less than the intiial size of the element in gslides
while (isOutsideBounds()) {
// decrease by 0.1 incrememnts until it fits within the width
fontSize = fontSize - 0.1;
// update the ctx with the new font style (shrinking the font size)
ctx.font = `${fontWeight} ${fontSize}pt ${fontFamily}`;
}
// returns the font size
return fontSize;
}
Then to use for a request to update the text after the text is inserted/replaced:
{
updateTextStyle: {
objectId: pageElement.objectId,
fields: 'fontSize',
style: {
fontSize: {
magnitude: calculateFontSize(pageElement, STRING_VALUE_HERE),
unit: 'PT'
}
}
}
}
Editing for specificity
The data in my geojson is formatted as string variables and I need to change those fields to numbers so my legend on my choropleth will read the data. I'm struggling to figure out where to use either the ParseFloat or Number functions so my leaflet map reads the data as a number.
I'm attempting to map each feature using different buttons. One button to show Population, another to show Average Water Bill, etc. I want the legend to show the data called on for each button. Below is the code I have for the legend and for the buttons...
// Global variables
let map;
let lat = 34;
let lon = -118;
let zl = 9;
let geojsonPath = 'https://raw.githubusercontent.com/LCIWaterProjects/DRAFT-LA-County-Governance-Map/main/data/Data_Update.geojson';
let geojson_data;
let geojson_layer;
let brew = new classyBrew();
let legend = L.control({position: 'bottomright'});
let info_panel = L.control();
let fieldtomap;
// initialize+
$( document ).ready(function() {
createMap(lat,lon,zl);
getGeoJSON();
});
// create the map
function createMap(lat,lon,zl){
map = L.map('map').setView([lat,lon], zl);
L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}',
{
attribution: 'Map data © OpenStreetMap contributors, Imagery © Mapbox',
maxZoom: 18,
id: 'light-v10',
tileSize: 512,
zoomOffset: -1,
accessToken: 'pk.eyJ1Ijoic2FyYWhwZXJlejEiLCJhIjoiY2t0MG9hZDNnMDZ2NDJ1c2M5dzBmb201OSJ9.5fv8NqX5cfA0NMcmEW_63g'
}).addTo(map);
}
// function to get the geojson data
$.getJSON(geojsonPath,function(data){
console.log(data)
// put the data in a global variable
geojson_data = data;
// call the map function
mapGeoJSON();
})
function mapGeoJSON(field,num_classes,color,scheme){
// clear layers in case it has been mapped already
if (geojson_layer){
geojson_layer.clearLayers()
}
// globalize the field to map
fieldtomap = field;
...
function createLegend(){
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
breaks = brew.getBreaks(),
labels = [],
from, to;
for (var i = 0; i < breaks.length; i++) {
from = breaks[i];
to = breaks[i + 1];
if(to) {
labels.push(
'<i style="background:' + brew.getColorInRange(to) + '"></i> ' +
from.toFixed(0) + ' – ' + to.toFixed(0));
}
}
div.innerHTML = labels.join('<br>');
return div;
};
legend.addTo(map);
// create buttons function
function myPopFunction(){
mapGeoJSON('Population',5,'YlOrRd','jenks');}
function myServeFunction(){
mapGeoJSON('Service_Co',5,'BuPu','quantiles');}
I need your help with the import of a set from an excel spreadsheet to CPLEX.
The set looks like this:
enter image description here
As you see there are two values for j = 2 and j = 11 each.
The goal is to import it as {int} to CPLEX so that it has the following structure:
enter image description here
I would be very thankful, if anybody could help me out with this.
Sincerely
you could rely on javascript within OPL
See https://github.com/AlexFleischerParis/oplexcel/blob/main/readsetincells.mod
{int} x=...;
{int} y=...;
{string} figures={"0","1","2","3","4","5","6","7","8","9"};
string cells[x][y]=...;
{int} values[x][y];
execute // parsing
{
for(var i in x) for(var j in y)
{
var s=cells[i][j];
var buffer="";
for(var k=0;k<s.length;k++)
{
if (figures.contains(s.charAt(k))) buffer=buffer+s.charAt(k);
else
{
if (buffer!="")
{
var value=Opl.intValue(buffer);
values[i][j].add(value);
buffer="";
}
}
}
}
}
execute display
{
for(var i in x) for(var j in y) if (Opl.card(values[i][j])!=0) writeln(i,",",j," => ",values[i][j]);
}
/*
which gives
2,12 => {4 5}
10,40 => {6}
*/
This use pulls us data from the post
https://www.instagram.com/p/{shortcode}/?__a=1
But I need a usage like this.
https://www.instagram.com/p/{postID}/?__a=1
How do I get this usage? What is the way to pull data over id?
This working.
class InstagramID
{
/**
* Base64 URL Safe Character Map.
*
* This is the Base64 "URL Safe" alphabet, which is what Instagram uses.
*
* #var string
*
* #see https://tools.ietf.org/html/rfc4648
*/
const BASE64URL_CHARMAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
/**
* Internal map of the results of all base10 digits (0-9) modulo 2.
*
* Used by the decimal-to-binary converter, to avoid costly bcmod() calls.
* Arranged by decimal offset, so the answer for decimal 9 is in index 9.
*
* #var string
*/
const BASE10_MOD2 = ['0', '1', '0', '1', '0', '1', '0', '1', '0', '1'];
/**
* Runtime cached bit-value lookup table.
*
* #var array|null
*/
public static $bitValueTable = null;
/**
* Converts an Instagram ID to their shortcode system.
*
* #param string|int $id The ID to convert. Must be provided as a string if
* it's larger than the size of an integer, which MOST
* Instagram IDs are!
*
* #throws \InvalidArgumentException If bad parameters are provided.
*
* #return string The shortcode.
*/
public static function toCode(
$id)
{
// First we must convert the ID number to a binary string.
// NOTE: Conversion speed depends on number size. With the most common
// number size used for Instagram's IDs, my old laptop can do ~18k/s.
$base2 = self::base10to2($id, false); // No left-padding. Throws if bad.
if ($base2 === '') {
return ''; // Nothing to convert.
}
// Left-pad with leading zeroes to make length a multiple of 6 bits.
$padAmount = (6 - (strlen($base2) % 6));
if ($padAmount != 6 || strlen($base2) === 0) {
$base2 = str_repeat('0', $padAmount).$base2;
}
// Now chunk it in segments of 6 bits at a time. Every 6 "digits" in a
// binary number is just 1 "digit" in a base64 number, because base64
// can represent the values 0-63, and 63 is "111111" (6 bits) in base2.
// Example: 9999 base10 = 10 011100 001111 base2 = (2, 28, 15) base64.
$chunks = str_split($base2, 6);
// Process and encode all chunks as base64 using Instagram's alphabet.
$encoded = '';
foreach ($chunks as $chunk) {
// Interpret the chunk bitstring as an unsigned integer (0-63).
$base64 = bindec($chunk);
// Look up that base64 character in Instagram's alphabet.
$encoded .= self::BASE64URL_CHARMAP[$base64];
}
return $encoded;
}
/**
* Converts an Instagram shortcode to a numeric ID.
*
* #param string $code The shortcode.
*
* #throws \InvalidArgumentException If bad parameters are provided.
*
* #return string The numeric ID.
*/
public static function fromCode(
$code)
{
if (!is_string($code) || preg_match('/[^A-Za-z0-9\-_]/', $code)) {
throw new \InvalidArgumentException('Input must be a valid Instagram shortcode.');
}
// Convert the base64 shortcode to a base2 binary string.
$base2 = '';
for ($i = 0, $len = strlen($code); $i < $len; ++$i) {
// Find the base64 value of the current character.
$base64 = strpos(self::BASE64URL_CHARMAP, $code[$i]);
// Convert it to 6 binary bits (left-padded if needed).
$base2 .= str_pad(decbin($base64), 6, '0', STR_PAD_LEFT);
}
// Now just convert the base2 binary string to a base10 decimal string.
$base10 = self::base2to10($base2);
return $base10;
}
/**
* Converts a decimal number of any size into a binary string.
*
* #param string|int $base10 The number to convert, in base 10. Must be 0+,
* a positive integer. And you must pass the
* number as a string if you want to convert a
* number that's larger than what fits in your
* CPU's integer size.
* #param bool $padLeft Whether to pad with leading zeroes.
*
* #throws \InvalidArgumentException If the input isn't a valid integer.
*
* #return string The binary bits as a string.
*/
public static function base10to2(
$base10,
$padLeft = true)
{
$base10 = (string) $base10;
if ($base10 === '' || preg_match('/[^0-9]/', $base10)) {
throw new \InvalidArgumentException('Input must be a positive integer.');
}
// Convert the arbitrary-length base10 input to a base2 binary string.
// We process as strings to support unlimited input number sizes!
$base2 = '';
do {
// Get the last digit.
$lastDigit = $base10[(strlen($base10) - 1)];
// If the last digit is uneven, put a one (1) in the base2 string,
// otherwise use zero (0) instead. Array is 10x faster than bcmod.
$base2 .= self::BASE10_MOD2[$lastDigit];
// Now divide the whole base10 string by two, discarding decimals.
// NOTE: Division is unavoidable when converting decimal to binary,
// but at least it's implemented in pure C thanks to the BC library.
// An implementation of arbitrary-length division by 2 in just PHP
// was ~4x slower. Anyway, my old laptop can do ~1.6 million bcdiv()
// per second so this is no problem.
$base10 = bcdiv($base10, '2', 0);
} while ($base10 !== '0');
// We built the base2 string backwards, so now we must reverse it.
$base2 = strrev($base2);
// Add or remove proper left-padding with zeroes as needed.
if ($padLeft) {
$padAmount = (8 - (strlen($base2) % 8));
if ($padAmount != 8 || strlen($base2) === 0) {
$base2 = str_repeat('0', $padAmount).$base2;
}
} else {
$base2 = ltrim($base2, '0');
}
return $base2;
}
/**
* Builds a binary bit-value lookup table.
*
* #param int $maxBitCount Maximum number of bits to calculate values for.
*
* #return array The lookup table, where offset 0 has the value of bit 1,
* offset 1 has the value of bit 2, and so on.
*/
public static function buildBinaryLookupTable(
$maxBitCount)
{
$table = [];
for ($bitPosition = 0; $bitPosition < $maxBitCount; ++$bitPosition) {
$bitValue = bcpow('2', (string) $bitPosition, 0);
$table[] = $bitValue;
}
return $table;
}
/**
* Converts a binary number of any size into a decimal string.
*
* #param string $base2 The binary bits as a string where each character is
* either "1" or "0".
*
* #throws \InvalidArgumentException If the input isn't a binary string.
*
* #return string The decimal number as a string.
*/
public static function base2to10(
$base2)
{
if (!is_string($base2) || preg_match('/[^01]/', $base2)) {
throw new \InvalidArgumentException('Input must be a binary string.');
}
// Pre-build a ~80kb RAM table with all values for bits 1-512. Any
// higher bits than that will be generated and cached live instead.
if (self::$bitValueTable === null) {
self::$bitValueTable = self::buildBinaryLookupTable(512);
}
// Reverse the bit-sequence so that the least significant bit is first,
// which is necessary when converting binary via its bit offset powers.
$base2rev = strrev($base2);
// Process each bit individually and reconstruct the base10 number.
$base10 = '0';
$bits = str_split($base2rev, 1);
for ($bitPosition = 0, $len = count($bits); $bitPosition < $len; ++$bitPosition) {
if ($bits[$bitPosition] == '1') {
// Look up the bit value in the table or generate if missing.
if (isset(self::$bitValueTable[$bitPosition])) {
$bitValue = self::$bitValueTable[$bitPosition];
} else {
$bitValue = bcpow('2', (string) $bitPosition, 0);
self::$bitValueTable[$bitPosition] = $bitValue;
}
// Now just add the bit's value to the current total.
$base10 = bcadd($base10, $bitValue, 0);
}
}
return $base10;
}
}
Usage:
$code = InstagramID::toCode($postID);
$Id = InstagramID::fromCode($postCode);
I have jest testing the following function (with Flowtype):
/**
* Returns an array of objects sorted a-z by prop.
*
* #prop array arr An array of objects.
* #prop array props An array of props to sort against.
* #return array A copy of arr sorted by prop.
*/
export function sortObjectsAz(arr: Array<Object>, props: Array<string>): Array<Object> {
if (arr.length < 1 || props.length < 1) return arr;
return [].concat(arr).sort((a, b) => {
var labelA = getSortProp(a, props);
var labelB = getSortProp(b, props);
return (labelA < labelB) ? -1 : (labelA > labelB) ? 1 : 0;
});
}
Jest coverage tells me I'm not fully testing the function due to return (labelA < labelB) not being tested.
How can I test this line effectively? I'm not sure as it is the method used by the sort function.