Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

echinopsis posted:

am I heading in the right direction?
That's a fine layout, though why a json file rather than a js module?

(The advantage of a module is webpack or whatever can make the whole thing into a single distribution file, rather than loading the game requiring multiple requests, and you can skip the json.decode step, you can just have a variable named JSONFile or a more appropriate name.)

Edit: Also, if you make it a module with a variable then you can unquote the dictionary keys, and the elements become fully typed objects, which a good editor might allow to be used for autocomplete and error correction.

roomforthetuna fucked around with this message at 00:01 on Jun 19, 2022

Adbot
ADBOT LOVES YOU

Analytic Engine
May 18, 2009

not the analytical engine
Looks good, just drop the two lines with } and { after crimes and put a comma there instead. Those are keys in the object

Future tip: JSON doesn't allow extraneous commas like JS does, and you can't serialize a non-dictionary object (becomes "[object]") or a circular dictionary

echinopsis
Apr 13, 2004

by Fluffdaddy

roomforthetuna posted:

That's a fine layout, though why a json file rather than a js module?

oo I never saw this

although I am thinking about importing a csv library and parsing a csv file at runtime because it’s much easier for me to maintain the data in excel and export to csv when I’m done and then that’s it, not another step

Analytic Engine posted:

Looks good, just drop the two lines with } and { after crimes and put a comma there instead. Those are keys in the object

Future tip: JSON doesn't allow extraneous commas like JS does, and you can't serialize a non-dictionary object (becomes "[object]") or a circular dictionary

about dictionaries

I finally worked out how to use them for ordinary key:value pairs

but what I want to know is if I can use a dictionary so that I can access a handful of variables via the dictionary key. eg


code:
dictionaryVariable[”primaryKey”].oneOfAFewVariables 
or maybe it’s addressed
code:
dictionaryVariable[primaryKey][secondaryKey]
or something


code:
let dictionaryVariable = [ primaryKey:[secondKeyA:”some stuff”, secondKeyB:”something else”]]
have a dictionary inside a dictionary?

I realise I’ve coded some stuff very poorly, and if I could create data structures that used keys to access them it’d be incredible

echinopsis
Apr 13, 2004

by Fluffdaddy
I used a basic dictionary for some functionality I recently added and man it just worked so easily lol

Doom Mathematic
Sep 2, 2008
echinopsis, you need someone to pair program with for an afternoon.

Video Nasty
Jun 17, 2003

Object.keys() will let you iterate over the JSON object and you can pull from that. Echi I can provide assistance for you tomorrow but I'm not on NZ time.

echinopsis
Apr 13, 2004

by Fluffdaddy

Doom Mathematic posted:

echinopsis, you need someone to pair program with for an afternoon.

I had literally thought about something like this, even paying someone for a couple hours or something, help me crack open some of these awful implementations of mine.

Video Nasty posted:

Object.keys() will let you iterate over the JSON object and you can pull from that. Echi I can provide assistance for you tomorrow but I'm not on NZ time.

I appreciate this, but it is a bit of a challenge to find a time given I've got kids and work and squeeze this in just when I can, but I might take you up on this if at some stage, if you're still keen and willing.

Osmosisch
Sep 9, 2007

I shall make everyone look like me! Then when they trick each other, they will say "oh that Coyote, he is the smartest one, he can even trick the great Coyote."



Grimey Drawer
I'm also happy to hop on and help, though I'm on CET. Plenty of time this weekend, or most evenings my time.

Good Sphere
Jun 16, 2018

Seems like I have a memory leak with images when using toDataURL. More specifically, I have a canvas element, and a button that calls the covertCanvasToImage() function. imageAsFile is just a div where an image element is added. imageToSave is global, and keeps being reused. No additional image elements are continuously added to the page, yet when I look at the sources in the inspector, new images keep being added, making the memory use go up and up. How do I keep it from leaking?

code:
var imageToSave = new Image(); 

function convertCanvasToImage() {
    imageToSave.src  = canvas.toDataURL("image/png");
    document.getElementById('imageAsFile').appendChild(imageToSave);
}

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Good Sphere posted:

How do I keep it from leaking?
Not sure if this is all, but appendChild in there means you're adding a new element every time. You probably want a corresponding removeChild to get rid of the one you added last time. Maybe like
code:
const parent = document.getElementById('imageAsFile');
parent.removeChild(parent.firstChild);
parent.appendChild(imageToSave);
Though I'm not really sure what happens when you put the same node into the same parent multiple times, or into multiple parents.

Alternatively you might just want to have the image object in the HTML to begin with, and then just set its src and not modify the tree at all.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
You're appending the Image element to the page every single time the function is called?

Good Sphere
Jun 16, 2018

I was just about to mention, even if I take care of removing it explicitly like that, it still has the same result leak.

code:

let imageAsFileEl = document.getElementById('imageAsFile')
if(imageAsFileEl.hasChildNodes())
{
    console.log(imageAsFileEl.firstChild)
    imageAsFileEl.removeChild(imageAsFileEl.firstChild);
}

imageToSave.src  = canvas.toDataURL("image/png")
imageAsFileEl.appendChild(imageToSave);

Jabor posted:

You're appending the Image element to the page every single time the function is called?

In either case, if you look at the elements in the inspector, there is only ever one image element.

roomforthetuna posted:

Alternatively you might just want to have the image object in the HTML to begin with, and then just set its src and not modify the tree at all.

This also leaks.

Good Sphere fucked around with this message at 19:02 on Jul 2, 2022

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
Oh, right, because you're making them via data src urls, they're probably ending up in the "file cache" sort of allocation. I don't think there's any way to tell the browser not to do that for data urls.

If you could switch to blob urls that might help you.

Good Sphere
Jun 16, 2018

roomforthetuna posted:

Oh, right, because you're making them via data src urls, they're probably ending up in the "file cache" sort of allocation. I don't think there's any way to tell the browser not to do that for data urls.

If you could switch to blob urls that might help you.

Thank you so much for helping me, but I've also tried using blob urls, lol. Maybe in a weird way? But the same result. Leak!
code:
// Global
var imageToSave = new Image(); // image to be saved
var temporaryImage;
var BASE64_MARKER = ';base64,';
var objectURL = window.URL || window.webkitURL;

function convertCanvasToImage() {

  // Remove old node
  let imageAsFileEl = document.getElementById('imageAsFile')
  if(imageAsFileEl.hasChildNodes())
  {
	imageAsFileEl.removeChild(imageAsFileEl.firstChild);
  }
	
  // Destroy old image
  if(temporaryImage) objectURL.revokeObjectURL(temporaryImage);

  // Create a new image from binary data
  var imageDataBlob = convertDataURIToBlob(canvas.toDataURL("image/png"));

  // Create a new object URL
  temporaryImage = objectURL.createObjectURL(imageDataBlob);

  // Set the new image
  imageToSave.src = temporaryImage;
  document.getElementById('imageAsFile').appendChild(imageToSave);
    
}

function convertDataURIToBlob(dataURI) {
  // Validate input data
  if(!dataURI) return;

  // Convert image (in base64) to binary data
  var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
  var base64 = dataURI.substring(base64Index);
  var raw = window.atob(base64);
  var rawLength = raw.length;
  var array = new Uint8Array(new ArrayBuffer(rawLength));

  for(i = 0; i < rawLength; i++) {
    array[i] = raw.charCodeAt(i);
  }

  // Create and return a new blob object using binary data
  return new Blob([array], {type: "image/png"});
}

necrotic
Aug 2, 2005
I owe my brother big time for this!
What if you use a new Image object each time, instead of just replacing the src attribute?

Good Sphere
Jun 16, 2018

necrotic posted:

What if you use a new Image object each time, instead of just replacing the src attribute?

Okay, I put var imageToSave = new Image(); right inside the convertCanvasToImage, and commented out the global one. Cleared cache. Refreshed. Same issue.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Good Sphere posted:

Thank you so much for helping me, but I've also tried using blob urls, lol. Maybe in a weird way? But the same result. Leak!
There were a couple of other suggestions on that stackoverflow link too - putting the image inside an iframe, and changing the iframe, apparently worked for some people when nothing else would. (Which makes sense because that makes it a separate page, so the caching behavior becomes like closing and opening the page.)

necrotic
Aug 2, 2005
I owe my brother big time for this!

Good Sphere posted:

Okay, I put var imageToSave = new Image(); right inside the convertCanvasToImage, and commented out the global one. Cleared cache. Refreshed. Same issue.

When you render a new image are you leaving the old one in the dom? You’d need to remove it to fully clean that up.

Good Sphere
Jun 16, 2018

roomforthetuna posted:

There were a couple of other suggestions on that stackoverflow link too - putting the image inside an iframe, and changing the iframe, apparently worked for some people when nothing else would. (Which makes sense because that makes it a separate page, so the caching behavior becomes like closing and opening the page.)

Good call. I've tried an iframe, and although it no longer continues to generate sources, it still fills up the memory footprint when looking at the network.



necrotic posted:

When you render a new image are you leaving the old one in the dom? You’d need to remove it to fully clean that up.

Yes I think I've tried that.

Here's something similar with removing the image, except I'm removing the iframe, which I'd think would release everything put inside the iframe:

code:
function convertCanvasToImage() {
	
	var iframe = document.createElement('iframe');
	
	// check if element has nodes
	let imageAsFileEl = document.getElementById('imageAsFile')
	if(imageAsFileEl.hasChildNodes()) // remove
	{
		console.log("has child nodes")
		$(imageAsFileEl).empty(); // trying jquery here
	}
	else // no children yet
	{
		console.log("no child nodes yet")
	}

	iframe.src = canvas.toDataURL("image/png")
	imageAsFileEl.appendChild(iframe);
	
	imageToSave.onload = function() {
	  URL.revokeObjectURL(imageToSave.src);
          imageToSave.onload = null;
        }
    
}

MrMoo
Sep 14, 2000

Images tend to build up waiting for garbage collection, this is why infinite scrolling with images is so broken everywhere. The CreateBitmap API has matching API to free resources, and is the only reliable method to do that. It is relatively new and not a lot of support, but I’m using it across browsers and is working.

Good Sphere
Jun 16, 2018

MrMoo posted:

Images tend to build up waiting for garbage collection, this is why infinite scrolling with images is so broken everywhere. The CreateBitmap API has matching API to free resources, and is the only reliable method to do that. It is relatively new and not a lot of support, but I’m using it across browsers and is working.

Ahh.. I hate to be needy, but I'd like to create an image element.

MrMoo
Sep 14, 2000

Canvas-only. I’m surprised no real solution for Image elements has been implemented.

Jim DiGriz
Apr 28, 2008

Maybe there is no room for guys like us.
Grimey Drawer
I've got a file that came in gzipped via a HTTP request and is currently sitting in the browser cache. PHP's gzdecode uncompresses it fine. However in Node.js, when I'm trying this code:

code:
        let buffer = fs.createReadStream(path.join(cachePath, filename));
        let gz = zlib.createUnzip();
        let output = fs.createWriteStream(path.join(outputPath, filename+'.pdf'));
        buffer.pipe(gz).pipe(output);
it throws an 'incorrect header check' error and stops, but not before generating an uncompressed file in outputPath, that has ~12k missing from the end. The partial result seems to be the same that PHP generated, only the ending is missing.

I've tried using the Sync methods, reading the file, unzipping and writing it, but the same error prevents the script from actually saving, of course.

Could it be that PHP's gzdecode has some error correction in it that the zlib library in Node.js does not? I've also tried pako.js, the errors are the same.

e: I've also tried it with a 6 times bigger file and the missing ending is still around ~12k. Don't know what this means yet, but it's apparently not a memory issue or a file error, but rather something with the stream reading maybe?

e2: setting chunkSize in createUnzip has an effect on the size of the missing file ending. Seems like it gives up on the last chunk.

Jim DiGriz fucked around with this message at 12:13 on Jul 4, 2022

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
I have a node story about fs that's a bit surprising. I have a few static files being served. I populate the set of valid files into a list first to make it trivial to avoid malicious attempts to read outside the target path, so I use fs.promise.readdir('client') to populate the set of files, and e.g. fs.promise.readFile('client/index.html') to read the file. The readdir works, but the readFile errors out on Linux with ENOENT (the readFile works on Windows).

What does work for readFile is fs.promise.readFile(path.join(process.cwd(), 'client/index.html'))

I'd just shrug it off and not even mention it, but it's bizarre that readdir works with the relative path but readFile doesn't. (Also, making it './client/index.html' doesn't help.)

MrMoo
Sep 14, 2000

Jim DiGriz posted:

I've got a file that came in gzipped via a HTTP request and is currently sitting in the browser cache. PHP's gzdecode uncompresses it fine. However in Node.js, when I'm trying this code:

code:
        let buffer = fs.createReadStream(path.join(cachePath, filename));
        let gz = zlib.createUnzip();
        let output = fs.createWriteStream(path.join(outputPath, filename+'.pdf'));
        buffer.pipe(gz).pipe(output);

Zip and GZip are two different things.

Jim DiGriz
Apr 28, 2008

Maybe there is no room for guys like us.
Grimey Drawer

MrMoo posted:

Zip and GZip are two different things.

Yes, I'm aware (zlib's createUnzip uses inflate/gunzip, depending on what it detects, but I've tried the specific methods as well, to no avail). The file(s) in question came via an XHTTPRequest gzipped, and as I mentioned, gzdecode works fine on them.

For now it seems that the last chunk the readstrem pipes into the decompressor makes it choke. I've even tried feeding it byte-by-byte, and it throws the same error. Setting the chunk size of the decompressor to 64 bytes proves this, as only the last 48 bytes are not decompressed from the input.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Are you sure the browser hasn't already unzipped it before storing it in the cache?

Jim DiGriz
Apr 28, 2008

Maybe there is no room for guys like us.
Grimey Drawer

Jabor posted:

Are you sure the browser hasn't already unzipped it before storing it in the cache?

Yes, 100%.

Jim DiGriz
Apr 28, 2008

Maybe there is no room for guys like us.
Grimey Drawer
Well, gently caress, I should have checked the actual file sooner. Firefox attaches the request data in plain text to the end of the gzipped file. PHP just shrugged it off but Node didn't. Thanks for the input, fellas.

frogbs
May 5, 2004
Well well well
I don't know why but the whole fetch api and .then stuff is absolutely destroying my brain today. I guess I'm used to more synchronous stuff where you grab the JSON from somewhere, turn it into an array and just iterate over it. The .then stuff and arrow functions are just not jiving with my brain.

I found some example code for working with the weather.gov api. The api returns an array of 'periods', each of which contain temperature and a forecast. I'm trying to iterate over each 'period' and then log (and eventually display) the temperature and forecast, but I just can't get the syntax right. What should I brush up on, arrow functions?

code:
    const latitude = 45.476466;
    const longitude = -122.72726;

    fetch(`https://api.weather.gov/points/${latitude},${longitude}`)
      .then(response => {
        if (!response.ok) {
          throw new Error('Unable to get points.');
        }
        return response.json();
      })
      .then(points => points.properties.forecastHourly)
      .then(url => fetch(url))
      .then(response => {
        if (!response.ok) {
          throw new Error('Unable to get forecast.')
        }

        return response.json();
      })
      .then(forecast => forecast.properties.periods)
      .then(period1 => period1[1].temperature)
      .then(temperature => {
        document.getElementById('temperature').innerText = temperature + '°';
        console.log(temperature);
      })
      .catch(error => alert('There was an error retrieving your weather.'));
I really just need to loop over forecast.properties.periods and output temperature, but no matter what syntax I try it doesn't want to do it. Should I forget the whole .then stuff after I get the data and just pass everything to a regular old function?

Apologies if this is a really dumb question.

Zephirus
May 18, 2004

BRRRR......CHK
-- Ignore welp

Zephirus fucked around with this message at 03:00 on Jul 21, 2022

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

frogbs posted:

I don't know why but the whole fetch api and .then stuff is absolutely destroying my brain today. I guess I'm used to more synchronous stuff where you grab the JSON from somewhere, turn it into an array and just iterate over it. The .then stuff and arrow functions are just not jiving with my brain.

I found some example code for working with the weather.gov api. The api returns an array of 'periods', each of which contain temperature and a forecast. I'm trying to iterate over each 'period' and then log (and eventually display) the temperature and forecast, but I just can't get the syntax right. What should I brush up on, arrow functions?

code:
    const latitude = 45.476466;
    const longitude = -122.72726;

    fetch(`https://api.weather.gov/points/${latitude},${longitude}`)
      .then(response => {
        if (!response.ok) {
          throw new Error('Unable to get points.');
        }
        return response.json();
      })
      .then(points => points.properties.forecastHourly)
      .then(url => fetch(url))
      .then(response => {
        if (!response.ok) {
          throw new Error('Unable to get forecast.')
        }

        return response.json();
      })
      .then(forecast => forecast.properties.periods)
      .then(period1 => period1[1].temperature)
      .then(temperature => {
        document.getElementById('temperature').innerText = temperature + '°';
        console.log(temperature);
      })
      .catch(error => alert('There was an error retrieving your weather.'));
I really just need to loop over forecast.properties.periods and output temperature, but no matter what syntax I try it doesn't want to do it. Should I forget the whole .then stuff after I get the data and just pass everything to a regular old function?

Apologies if this is a really dumb question.

That looks right to me. I can successfully get to the final .then block only to bomb on document.getElementById('temperature').innerText

You could definitely use less .then blocks and consolidate all your logic into one block or even use async/await, which might help you visualize what you're doing better:


TypeScript code:
const paintForecastToScreen = async () => {
  try {
    /*
      fetch the weather and convert to JSON and access the forecast endpoint
    */
    const weatherResponse = await fetch(`https://api.weather.gov/points/${latitude},${longitude}`)
    const weatherAsJSON = await weatherResponse.json()
    const forecastEndpoint = weatherAsJSON.properties.forecastHourly;

    /* 
      fetch again the new forecast API and get the list of temps
    */
    const forcastResponse = await fetch(forecastEndpoint);
    const forecastResponseAsJSON = await forcastResponse.json()


    /* 
      finally, iterate over all the periods, access the temp in each object
      and do something with it
    */
    const periods = forecastResponseAsJSON.properties.periods

    for (let i = 0; i < periods.length; i++) {
      const eachTemperature = periods[i].temperature

      /* do something with the temp here */
    }
  } catch (e) {
    console.log(e);
    alert('There was an error retrieving your weather.')
  }
}

paintForecastToScreen()

frogbs
May 5, 2004
Well well well

teen phone cutie posted:

That looks right to me. I can successfully get to the final .then block only to bomb on document.getElementById('temperature').innerText

You could definitely use less .then blocks and consolidate all your logic into one block or even use async/await, which might help you visualize what you're doing better:


TypeScript code:
const paintForecastToScreen = async () => {
  try {
    /*
      fetch the weather and convert to JSON and access the forecast endpoint
    */
    const weatherResponse = await fetch(`https://api.weather.gov/points/${latitude},${longitude}`)
    const weatherAsJSON = await weatherResponse.json()
    const forecastEndpoint = weatherAsJSON.properties.forecastHourly;

    /* 
      fetch again the new forecast API and get the list of temps
    */
    const forcastResponse = await fetch(forecastEndpoint);
    const forecastResponseAsJSON = await forcastResponse.json()


    /* 
      finally, iterate over all the periods, access the temp in each object
      and do something with it
    */
    const periods = forecastResponseAsJSON.properties.periods

    for (let i = 0; i < periods.length; i++) {
      const eachTemperature = periods[i].temperature

      /* do something with the temp here */
    }
  } catch (e) {
    console.log(e);
    alert('There was an error retrieving your weather.')
  }
}

paintForecastToScreen()

Thank you so much! This is so much cleaner and readable. Will mess with it more tonight. I really appreciate the help!

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

frogbs posted:

Thank you so much! This is so much cleaner and readable. Will mess with it more tonight. I really appreciate the help!

no worries. also as an enhancement, you could make that function return a promise or promise rejection and then chain a .then or .catch off of it like so:

TypeScript code:
const getTemps = async () => {
  try {
    const latitude = 45.476466;
    const longitude = -122.72726;
    /*
      fetch the weather and convert to JSON and access the forecast endpoint
    */
    const weatherResponse = await fetch(`https://api.weather.gov/points/${latitude},${longitude}`)
    const weatherAsJSON = await weatherResponse.json()
    const forecastEndpoint = weatherAsJSON.properties.forecastHourly;

    /* 
      fetch again the new forecast API and get the list of temps
    */
    const forcastResponse = await fetch(forecastEndpoint);
    const forecastResponseAsJSON = await forcastResponse.json()


    /* 
      finally, iterate over all the periods, access the temp in each object
      and do something with it
    */
    const periods = forecastResponseAsJSON.properties.periods

    return periods;
  } catch (e) {
    return Promise.reject('There was an error retrieving your weather.')
  }
}

getTemps()
  .then(periods => {
    /* do stuff with periods */
  })
  .catch(errorMessage => {
    /* do something with the erro */
  })

fakemirage
Nov 6, 2012

Can't see the haters.

frogbs posted:

I really just need to loop over forecast.properties.periods and output temperature, but no matter what syntax I try it doesn't want to do it.
You were probably looking for map or reduce (other options are available as well).
JavaScript code:
  //...
  .then((forecast) => forecast.properties.periods)
  .then(periods => periods.map(period => period.temperature)) // returns an array of temperatures e.g. [62, 60, 59, ...]
  .then(temperatures => {
    document.getElementById('temperature').innerText = temperatures.map(temperature => `${temperature}°`);
  })
  // ...
But as teen phone cutie pointed out, you could probably simplify things by combining some of your then blocks.
JavaScript code:
  //...
  .then(forecast => {
    const temperatures = forecast.properties.periods.map(period => `${period.temperature}°`);
    document.getElementById('temperature').innerText = temperatures;
  })
  // ...

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice
How would I do this without creating the array and pushing to it in Typescript:

JavaScript code:
type Blah = {
 a: number;
 b: number;
 c: number;
};


const foo: Blah = { a: 0, b: 1, c: 0 };

const getKeysThatAreZero = (input: Blah)  => {
  // this seems messy as all get out
  const zeroKeys: (keyof Blah)[] = [];
  let k: keyof Blah;
    for (k in input) {
      if(input[k] === 0) {
	zeroKeys.push(k)
      }
    }
  return zeroKeys;
}

getKeysThatAreZero(foo); // ["a", "c"]
I've tried doing things like:

JavaScript code:
Object.keys(input).filter(key => input[key] === 0);
But this makes typescript sad.

EDIT: the magic of posting on the internet worked yet again:

JavaScript code:
(Object.keys(input) as Array<keyof Blah>).filter(key => input[key] === 0);
Works like a charm.

Lumpy fucked around with this message at 19:23 on Jul 26, 2022

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself
yeah normally you would just need to type the function argument like you were and it should work, but it looks like .keys has a bad TS definition out of the box.

the TS def for keys should go from this:



to this:



if anyone here wants to fix that and have some open source bragging rights lol

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice
Here's one I am stumped on. I need to remove the multiple keys from an array of objects. If they are fixed, it is simple:

JavaScript code:
const thing = [
{ a: 0, b: 1, c: 0, d: 9, e: 7},
{ a: 4, b: 2, c: 9, d: 3, e: 2},
];
thing.map(({b, d, ...keepers}) => keepers); // [ { a: 0,  c: 0, e: 7}, { a: 4,  c: 9, e: 2} ]
With one key, you can pass it in dynamically like so:
JavaScript code:
const removeAKey = (thing, remove) => thing.map(({[remove]: _, d, ...keepers}) => keepers);

removeAKey(thing, "a"); //[ { b: 1, c: 0, d: 9, e: 7}, { b: 2, c: 9, d: 3, e: 2} ]
However, I've tried many, many variations on this:
JavaScript code:
const removeManyKeys = (thing, removeMany) => thing.map(({
...removeMany,  // or [removeMany], or [...removeMany], or lots more floundering
...keepers}) => keepers);

removeManyKeys(thing, ["a", "d"]);  // BOOM 
but how can one remove multiple dynamic keys? Obviously I could just remove them all one at a time, but I feel like there has to be a way to do this simply. Or maybe not!

teen phone cutie
Jun 18, 2012

last year i rewrote something awful from scratch because i hate myself

something like this?

TypeScript code:
const removeManyKeys = <T extends Record<string, any>, R extends keyof T>(things: T[], removeMany: R[]): Omit<T, R>[] =>
    things.reduce((acc, value) => {
        const copiedValue = { ...value }
        for (let i = 0; i < removeMany.length; i++) {
            delete copiedValue[removeMany[i]]
        }
        return [...acc, copiedValue]
    }, [] as T[])

teen phone cutie fucked around with this message at 20:46 on Jul 26, 2022

Adbot
ADBOT LOVES YOU

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

teen phone cutie posted:

something like this?

TypeScript code:
const removeManyKeys = <T extends Record<string, any>, R extends keyof T>(things: T[], removeMany: R[]): Omit<T, R>[] =>
    things.reduce((acc, value) => {
        const copiedValue = { ...value }
        for (let i = 0; i < removeMany.length; i++) {
            delete copiedValue[removeMany[i]]
        }
        return [...acc, copiedValue]
    }, [] as T[])


I was trying to avoid loops, but that works. However, since someone else on the team already is using Ramda, I just wound up cheating:

JavaScript code:
import { omit } from 'ramda';

const keysToRemove = ["a", "b"];
thing.map(item => omit(keysToRemove, item); // I can at least hide the loops! :v:

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply