Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
252 views
in Technique[技术] by (71.8m points)

javascript - Return to parent object in lodash chaining "_.chain()"

I'm using a library (lowdb) that uses lodash under the hood to make a local database in a json file. The thing I want to know is how to get to the parent object in lodash chaining after modifying child to modify another one, eg.

const low = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync');

const adapter = new FileSync('db.json');
const db = low(adapter);

/*
Eg. DB structure:
{
  "posts": [
    {
      "id": 1,
      "title": "post1"
    },
    {
      "id": 2,
      "title": "post2"
    },
    {
      "id": 3,
      "title": "post3"
    }
  ]
}
*/

db
  .get('posts')
  .find({ id: 2 })
  .assign({ title: 'edited title' }) 
  //after that I want to go back to posts to edit another one in the same chain
  .write();

I know that it could be done in multiple calls but I was wondering if it's possible to do it in one chain.

question from:https://stackoverflow.com/questions/65920839/return-to-parent-object-in-lodash-chaining-chain

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Short answer

If your goal is to make your code as good looking as possible, I would recommend you use lodash-id, and its updateById function. I introduce all kind of possible solutions in my long answer, but personally I would avoid chaining directly, and instead do something like:

const lodashId = require('lodash-id');
const low = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync');

const adapter = new FileSync('db.json');
const db = low(adapter);
db._.mixin(lodashId);

const posts = db.get("posts");
posts.updateById( 1, { title: 'edited title 1' }).commit();
posts.updateById( 2, { title: 'edited title 2' }).commit();
posts.updateById( 3, { title: 'edited title 3' }).commit();
posts.write();

Long answer

There are a few ways to approach this problem, and updateById is not the only way to do it by far. As a matter of fact, it is also possible to do this in one chain by using the tap function. Below I introduce a few alternatives.

Can I quickly try those functions in the browser?

Of course! I actually made a Codepen where you can try all of the different possible approaches directly in your browser, just remember to open your browser console to see the test results! Here is the link: https://codepen.io/jhack_jos/pen/XWNrpqX

How do I call the functions?

All those functions receive an adapter as input. You can call them this way:

const result = testcaseXY(adapter);

1A) tap & find+assign

function testcase1A(adapter)
{
  const db = low(adapter);
  return db
    .get("posts")
    .tap( (posts) => _.chain(posts).find({ id: 1 }).assign({ title: 'edited title 1' }).commit())
    .tap( (posts) => _.chain(posts).find({ id: 2 }).assign({ title: 'edited title 2' }).commit())
    .tap( (posts) => _.chain(posts).find({ id: 3 }).assign({ title: 'edited title 3' }).commit())
    .write();
}

What is tap?

  • it's a utility function from lodash
  • it does not directly change the database
  • it does, however, pass a reference to the object hold to the interceptor function (in this example I used an arrow function)
  • the reference to the value can be changed, thus affecting the object inside the database
  • calling .write() on the database makes the changes definitive.

2A) tap & find+set

function testcase2A(adapter)
{
  const db = low(adapter);
  _.mixin(lodashId);
  return db
    .get("posts")
    .tap( (posts) => _.chain(posts).find({ id: 1 }).set("title", 'edited title 1').commit())
    .tap( (posts) => _.chain(posts).find({ id: 2 }).set("title", 'edited title 2').commit())
    .tap( (posts) => _.chain(posts).find({ id: 3 }).set("title", 'edited title 3').commit())
    .write();
}

What is set?

  • it's a slightly shorter way to do what .assign does, but only with one property.
  • you may expect .set to be faster then .assign

3A) tap & updateById

function testcase3A(adapter)
{
  const db = low(adapter);
  _.mixin(lodashId);
  return db
    .get("posts")
    .tap( (posts) => _.chain(posts).updateById( 1, { title: 'edited title 1' }).commit())
    .tap( (posts) => _.chain(posts).updateById( 2, { title: 'edited title 2' }).commit())
    .tap( (posts) => _.chain(posts).updateById( 3, { title: 'edited title 3' }).commit())
    .write();
}

What is updateById?

  • it's a function that is exposed by the lodash-id library
  • you must first add it to lowdb as a mixin
  • to do so you first need to require it with const lodashId = require('lodash-id')
  • then you need to call db._.mixin(lodashId);
  • here instead I am calling directly _.mixin(lodashId), because I am directly using lodash inside the tap function, without going through lowdb

1B) temporary variable & find+assign

function testcase1B(adapter)
{
  const db = low(adapter);
  const posts = db.get("posts");
  posts.find({ id: 1 }).assign({ title: 'edited title 1' }).commit();
  posts.find({ id: 2 }).assign({ title: 'edited title 2' }).commit();
  posts.find({ id: 3 }).assign({ title: 'edited title 3' }).commit();
  return posts.write();
}

As you may see, using a temporary variable here gives us a more compact code, which could be easier to read, debug, and refactor.

2B) temporary variable & find+set

function testcase2B(adapter)
{
  const db = low(adapter);
  const posts = db.get("posts");
  posts.find({ id: 1 }).set("title", 'edited title 1').commit();
  posts.find({ id: 2 }).set("title", 'edited title 2').commit();
  posts.find({ id: 3 }).set("title", 'edited title 3').commit();
  return posts.write();
}

3B) temporary variable & updateById

function testcase3B(adapter)
{
  const db = low(adapter);
  db._.mixin(lodashId);
  const posts = db.get("posts");
  posts.updateById( 1, { title: 'edited title 1' }).commit();
  posts.updateById( 2, { title: 'edited title 2' }).commit();
  posts.updateById( 3, { title: 'edited title 3' }).commit();
  return posts.write();
}

Thank you for reading!

I'd be glad to give any further explanation, may you need it. As a bonus I would add it could be possible to write some clever utility function as a lowdb/lodash mixin in order to enable us to have a shorter syntax and still properly chain. That is, however, probably more than what you were looking for.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...