[Solved] Correctly build a filter function without DRY issue

Hi all

I have some json objects which has one parameter called ‘cat’ I’m trying to build a simple function so I can run the code by… example:

catFunction(food);
catFunction(drink);

At the moment I have the objects showing in the console using below but how can I build it so I don’t have to repeat everything:

var result = json.filter(function(obj) {
  return obj.cat == "drink";
});
console.log(result);

var result2 = json.filter(function(obj) {
  return obj.cat == "food";
});
console.log(result2);

I’m wondering the correct way to build a function so I can drop in the name of the cat so I can use and display content?

jsFiddle example

And hoping to display things with:
document.getElementById("example").innerHTML += ....

Put shared logic in a function:

function catFunction(json, name) {
  return json.filter(function(obj) {
    return obj.cat == name;
  });
}

var result = catFunction(json, "drink");
var result2 = catFunction(json, "food");

You could have json as a global variable accessible to catFunction so you don’t need to pass it in but it’s better to be explicit.

2 Likes

Perfect! :smile:

So the idea now is when I render example:

var result = catFunction(json, "drink");

I will print out a list of the objects for that category inside a list inside <ul id="food">, food example:

<ul id="food">
<li>Title One</li>
<li>Title Two</li>
</ul>

Is it possible, easy to add document.getElementById("?????").innerHTML += ... into the mix?

I’ll be printing these on the same page so I’ll need different containers for each list, hence the ??? above, maybe a little more tricky?

I might also be adding more data into the json - meaning I’ll print more than just the title.

Example:

  • Title One
    Food
    Address
  • Title Two
    Food
    Address

Thanks again for advice so far, just what I’m after :sunglasses:

Updated jsfiddle

Barry

If I understand you correctly, you might generate a ul element with the corresponding ID right within @markbrown4’s catFunction(), and directly reduce the filter results into that element. Like


var container = document.getElementById('my-container');

function catFunction(json, name) {

  return '<ul id="' + name +'">' +
    json.filter(function(obj) {
      return obj.cat === name;
    }).reduce(function(prev, curr) {
      return prev + '<li>' + curr.title +
        '<br>' + curr.cat + '</li>';
    }, '') + '</ul>';
}


container.innerHTML = catFunction(json, 'food');
container.innerHTML += catFunction(json, 'drink');

(Urgh that looks rather messy with all those string concatenations. ^^ You might use ES6 template literals and arrow functions instead to make it a bit tidier.)

Edit: Okay here’s a somewhat more readable version:

function catFunction(json, name) {
  var list = json
    .filter(obj => obj.cat === name)
    .reduce((prev, curr) => prev + 
      `<li>
        ${curr.title}<br>
        ${curr.cat}
      </li>`, '');
      
  return `<ul id="${name}">${list}</ul>`;
}

fiddle

If I understand you correctly, you might generate a ul element with the corresponding ID right within @markbrown4’s catFunction(), and directly reduce the filter results into that element.

Yes exactly that m3g4p0p thanks.

(Urgh that looks rather messy with all those string concatenations. ^^ You might use ES6 template literals and arrow functions instead to make it a bit tidier.)

Ha impressive :grinning:

I’ve only just started using a few of ES5 latest features this is looking good. Very clean and where I’ll be heading once I understand things, thanks for detailed info.

I read if using ES5/6 we’ll need to use polyfill for IE<9?

And if I want to display the lists inside diffrent conatiners, do something like -
http://jsfiddle.net/n2mqpwj8/3/ ?

Barry

Yeah, I’d probably keep those functions separate but the same idea.

function catFunction(json, name) {
  return json.filter((obj)=> obj.cat == name)
}

function render(name, list) {
  return `
    <ul id="${name}">
      ${list.map((item)=> (
        `<li>
          ${item.title}<br>
          ${item.cat}
        </li>`
      ))}
    </ul>
  `
}

var result = catFunction(json, 'drink');
container.innerHTML = render(result, 'drink');

Advanced stuff!

Thanks guys really put me on track regarding best practices here using the latest code, appreciated a couple of good options :sunglasses:

And what about what I mention above:

I read if using ES5/6 we’ll need to use polyfill for IE<9?

Do yourselves use polyfills when coding with ES5/6? Or don’t you bother? Not really a concern?

Thanks, Barry

Everyone has a different range of browsers that they have to / want to support.
I only use them where testing shows that we need to provide the support for certain older browsers.

One of the issues with polyfills is that they increase page loading time, so the less of them you can get away with the better.

######And yes, my inner grammarian says that I should use “fewer of them” instead, but I’m a rebel.

Everyone has a different range of browsers that they have to / want to support.

Absolutely. This is one of the main problems, some people want everything to work perfect on IE8/9 amongst other things :frowning:

But yes, use whats best for the job, what’s needed at the time. Worse case scenario. your data does not show ha… though maybe not a good idea if you have a paying client.

One of the issues with polyfills is that they increase page loading time, so the less of them you can get away with the better.

I second that.

I was just writing another reply m3g4p0p… you removed the last post? Some good information I was reading.

And been checking out:

http://kangax.github.io/compat-table/es5/
http://kangax.github.io/compat-table/es6/

Seems like lots more support for ES5… not the best mobile support for ES6 if you don’t want to use a polyfill.

Anyhow, thanks!

You can’t really polyfill es6 as it changes syntax, you will want to compile it down to es5 using browserify or webpack.

Cool thanks markbrown4… I’ll look into this once I get an understand of what I’ll finally use and support.

Great!

Thanks again, still learning lots of this :expressionless:

Ah sorry I was trying to further refactor that snippet, but it resulted in even worse spaghetti code… :-D Anyway, if you think it’s interesting here are another 2 cents of mine…

I think separating the functions as @markbrown4 suggested is a very good idea as it makes them more reusable; however you might call catFunction() from within render(), so that you don’t have to pass the same name string twice. Maybe you could use render() as a mere wrapper function… so while we’re at it, here’s another possibility:

var container = document.getElementById('my-container');

var getCat = function(array, cat) {
  return array.filter(el => el.cat === cat);
};

var getList = function(array, id) {
  var list = array.reduce((prev, curr) => prev + 
    `<li>
      ${curr.title}<br>
      ${curr.cat}
    </li>
    `, '');

  return `<ul id="${id}">${list}</ul>`;
};

var render = function(json, cat) {
  var result = getCat(json, cat);

  return getList(result, cat);
};

container.innerHTML = render(json, 'food');

Cheers m3g4p0p, thanks for getting back :sunglasses:

It’s mainly so I can get an overall understanding of different techniques and best way to tackle different challenges, best tool, snippets to use. As above, you have used a number of different functions to organise the data a little more which will become more useful when dealing with bigger datasets, a good technique.

Lots of great information in this post, a good variety which I can use once I understand things a little more, which I’ll use in reference.

Signing out and thanks all!

You can’t polyfill half of ES7 (released last month) either.

That’s just these two features right?

As a result, for the next 5 to 10 years we’ll be compiling ES6 and ES7 code down to ES5, not using several coding techniques that we’ll tell ourselves are for compatibility reasons. We’ll fool ourselves into thinking that we’re writing really good code when the browsers will really be having to deal with the compiled ES5 code instead.

All because we fear breaking the web. Watch that “fear” word - it retards far too many things.

1 Like

No, compiling down to es5 is just pragmatic right now and it’s not difficult to do.

JS compilers like Babel could take on an approach like Autoprefixer where only the features that need fallbacks are compiled down, when browser support is solid then it stops being translated to something else.

Most of the time, It’s right to be concerned about breaking the web.

1 Like

One question that has arisen.

How do I capture the unique index of each object, even after sorting so the index doesn’t change?
As I’ll be linking to items using the value of index.

Something like:
curr.index - which says ‘undefined’

And is \n \t needed (which was added using Babel)?

function catFunction(json, name) {
  var list = json.filter(function (obj) {
    return obj.cat === name;
  }).reduce(function (prev, curr) {
    return prev + ('<li><a href="' + curr.index + '">\n      \t' + curr.title + '</a></li>');
  }, '');

  return '<h3>' + name + '</h3><ul>' + list + '</ul>';
}

Thanks, Barry

If you don’t want the index to change, you might add it as a property to each object when fetching the data, like

json.forEach((value, index) => {value.index = index});

This way the index could be an arbitrary URL as well.