Tag-System
Tag System result in front end

Intro:

Ok, lets assume that we have a form with an input that accepts a string, this one then can be used to create tags……strings separated by comma. Take a look to it:

string1, string2, ,string4, string5,string 6,

Errors:

With that being said, there are some errors on it. Not a big deal to some developers and not an obstacle when posting stuff to the Internet but this will create an array with empty objects….actually if this gets sent to our MongoDB, we will end up with this:

tags: [
  "string1",
  "string2",
  "",
  "string4",
  "string5",
  "string 6",
  ""
]

As shown above, the errors found in our string are:

  • The empty object after string2
  • the string 6 with an added space(there should be a hyphen instead),
  • and the empty object created after the last comma.

The fun begins:

I’m sure there’s no need to mention this but in order to separate our string into multiple objects, we need our split() and map() functions.

const tags = 'string1, string2, ,string4, string5,string 6,';
tags.split(',').map((tag) => tag.trim())

Map is basically running through each object in our string by using split which basically creates a new object per each comma found.

Filter()

Initially, I’d like to get ridd off the empty objects; for that we need some type of filtering which will make sure the lenght of the string is not equal to 0.

const tags = 'string1, string2, ,string4, string5,string 6,';
tags.split(',').map((tag) => tag.trim()).filter((tag) => tag.length !== 0);

This creates something totally amazing, it returns a new array with no empty objects, YAY:

tags: [
  "string1",
  "string2",
  "string4",
  "string5",
  "string 6"
]

However, there is still one big error that needs work…. Do you have a guess?

Strings with spaces are usually good when they are supposed to be read in X website, let’s say, an article, a blog, the lyrics of a song, a book(maybe?),etc. This post talking shit that nobody might even read it?, lol.

The problem comes when in X article there are some tags on it that can be clicked by the user, which then redirects the user to the posts related to said tag…..I’m sure you have seen a lot of them, even my website uses them!. The URL created in the new webpage might look like this(assuming string1 has a space):

example.com/posts?keyword=string%201

Looks horrible, don’t you think?…and as far as I know, browser are still unable to replace spaces…..so that’s where people like me come to make the structures a bit nicer.

My goal is to create this URL for each single tag with spaces-in-between:

example.com/posts?=keyword=string-1

Basically replacing the space with a hyphen. One the things that I love about programming languages is that we can manipulate strings in many many ways; JavaScript has already given us a good look at split() and filter(), now what we need is replace().

const tags = 'string1, string2, ,string4, string5,string 6,';
tags.split(',').map((tag) => tag.trim().replace(/[\s]+/g, '-')).filter((tag) => tag.length !== 0);

The way in which I used replace() works in this way:

I used a ‘[]‘ to let it know that I might want to remove more than just spaces, if you are sure you’re only looking to remove spaces, then remove the []. I also used the g which makes reference to the global object.

What we usually call space, is a character found within the special characters, the most populars are:

  • \s – space
  • \t – tab
  • \n – new line

With all of this done, our array should now look like this:

tags: [
  "string1",
  "string2",
  "string4",
  "string5",
  "string-6"
]

Most developers would agree that our array looks perfect and that is right, right? I mean we have done everything we wanted regarding our string but……….do you remember our task list?:

  • The empty object after string2
  • the string 6 with an added space(there should be a hyphen instead),
  • and the empty object created after the last comma.

…but what if, I mean what if our string 6 contained more than one single space?. The current function would make our string look like this if multiple spaces are found: string-------------------------------------------------------6 and that shit, just Gosh look horrible everytime I see it in an URL. Instead of having multiple hyphen, we just need one, just one single hyphen, that is it!. Take a look in our final version and tell me what’s the difference?:

let tags = 'string1, string2, ,string4, string5,string 6,';
tags
.split(',')
.map((tag) =>
  tag
    .trim()
    .replace(/ +/g, ' ')
    .split(' ')
    .join('-')
    .toLowerCase()
)
.filter((tag) => tag.length !== 0);
console.log(tags);

Este es el producto final:

Tag-System
Tag System result in front end

Oh yes, I almost forget about what the whole code might look like in X backend when using this functionality:

exports.createOne = (Model) =>
  asyncHandler(async (req, res, next) => {
    // Add user to req,body
    req.body.user = req.user._id;
    if (req.body.tags) {
      req.body.tags = Array.isArray(req.body.tags)
        ? req.body.tags
        : req.body.tags
            .split(',')
            .map((tag) =>
              tag
                .trim()
                .replace(/ +/g, ' ')
                .split(' ')
                .join('-')
                .toLowerCase()
            )
            .filter((tag) => tag.length !== 0);
    }

    // Create document
    const doc = await Model.create(req.body);

    // Send response
    res.status(201).json({ success: true, data: doc });
  });

Also this function is perfect to create a function to fetch ‘related posts’:

// @desc    Get all related posts according to :id
// @desc    You NEED TO SEND A STRING CONTAINING ALL THE TAGS
// @desc    ARRAY FROM THE FRONT-END OF YOUR APP
// @route   GET /api/v1/videos/:id/related
// @access  Private
// @status  DONE
exports.getRelatedVideos = asyncHandler(async (req, res, next) => {
  const video = await Video.findById(req.params.id)

  if (!req.params.id.match(/^[0-9a-fA-F]{24}$/) || !video) {
    return next(
      new ErrorResponse(`Document not found with id of ${req.params.id}`, 404)
    )
  }

  const related = await Video.find({
    _id: { $ne: req.params.id },
    tags: { $regex: req.query.tags.split(',').join('|'), $options: 'i' },
  })
    .select('title slug text views thumbnail producer createdAt')
    .populate({
      path: 'producer',
      select: 'title avatar',
    })
    .limit(8)

  if (!related) {
    return next(
      new ErrorResponse(
        `No documents related to id of ${req.params.id} found`,
        404
      )
    )
  }

  res.status(200).json({ success: true, data: related })
})

The trick is found in these two lines

_id: { $ne: req.params.id },
tags:{ $regex:req.query.tags.split(',').join('|'),$options: 'i' },

With that being said, my code might be a bit different to yours but hopefully will give you a hint in creating this type of tags.

That’s it for today bois. Gracias y Adios-Adios 🙂 . Estoy a punto de hecharme uno bien ‘puerco’ y necesitare mis dos manos xD.

Leave a Reply

Back to Top