5

I have an array of wines containing objects with data for each wine:

var wines = [
  { _id: '59a740b8aa06e549918b1fda',
    wineryName: 'Some Winery',
    wineName: 'Pinot Noir',
    wineColor: 'Red',
    imageLink: '/img/FortBerensPN.png' },
  { _id: '59a7410aaa06e549918b1fdb',
    wineryName: 'Some Winery',
    wineName: 'Pinot Gris',
    wineColor: 'White',
    imageLink: '/img/FortBerensPG.png' },
  { _id: '59a74125aa06e549918b1fdc',
    wineryName: 'Some Winery',
    wineName: 'Rose',
    wineColor: 'Rose',
    imageLink: '/img/FortBerensRose.png' },
  { _id: '59a74159aa06e549918b1fdd',
    wineryName: 'Some other Winery',
    wineName: 'Rose',
    wineColor: 'Rose',
    imageLink: '/img/FortBerensRose.png' },
  { _id: '59a7417aaa06e549918b1fde',
    wineryName: 'Some other Winery',
    wineName: 'Pinot Gris',
    wineColor: 'White',
    imageLink: '/img/FortBerensPG.png' },
  { _id: '59a8721f4fd43b676a1f5f0d',
    wineryName: 'Some other Winery',
    wineName: 'Pinot Gris',
    wineColor: 'White',
    imageLink: '/img/FortBerensPG.png' },
  { _id: '59a872244fd43b676a1f5f0e',
    wineryName: 'Winery 3',
    wineName: 'Pinot Noir',
    wineColor: 'Red',
    imageLink: '/img/FortBerensPN.png' } ]

I can figure out how to search -- case insensitive -- for a wine object, while specifying which key of the object to search in, like this:

var search = 'Noir'

filteredWines = function () {
  return wines.filter(function(wine){
    return (wine.wineName.toLowerCase().indexOf(search.toLowerCase())>=0;
  });
};

Returns:

[ { _id: '59a740b8aa06e549918b1fda',
    wineryName: 'Some Winery',
    wineName: 'Pinot Noir',
    wineColor: 'Red',
    imageLink: '/img/FortBerensPN.png' },
  { _id: '59a872244fd43b676a1f5f0e',
    wineryName: 'Winery 3',
    wineName: 'Pinot Noir',
    wineColor: 'Red',
    imageLink: '/img/FortBerensPN.png' } ]

However, if var search = 'Winery 3' or var search = 'red' then it will obviously return no results, as it's looking in the value of wineName of each object in the array.

So is there a way to use filter (or another method?) to search through all key values, or even better, multiple specified key values and return an array of the matching objects?

Something like:

filteredWines = function () {
  return wines.filter(function(wine){
    return ((wine.wineName.toLowerCase() && wine.wineName.toLowerCase() 
          && wine.wineName.toLowerCase()).indexOf(search.toLowerCase())>=0;
  });
};

Or am I completely barking up the wrong tree?

PS. I'm using Vue.js 2 so if there's a better way inside vue then I'm all ears!

4
  • Should each property be searched for one value? Or are you specifying a different search value for each property? Commented Aug 31, 2017 at 23:10
  • Maybe I'm misunderstanding your question, but I think simply using an OR operator would work? You could clean up this part by creating a function (wine.wineName.toLowerCase().indexOf(search.toLowerCase())>=0. Or, you could use a regex. Commented Aug 31, 2017 at 23:20
  • BTW, take your pick from whatever filter method people end up posting, but a computed property would be the ideal way to use it in Vue. Commented Aug 31, 2017 at 23:27
  • @Bert I was stuffing this in a computed property and returning the results :) Thanks for making that clear for others though! Commented Sep 1, 2017 at 1:32

5 Answers 5

23

You could have a more generic function that will scan all the properties for the string. Loop through all property values with Object.values() and use some to bail out as soon as you have a match:

filteredWines = function (search) {
    var lowSearch = search.toLowerCase();
    return wines.filter(wine =>
        Object.values(wine).some(val => 
            String(val).toLowerCase().includes(lowSearch) 
        )
    );
}

If you prefer to pass specific keys to search in:

filteredWines = function (search, keys) {
    var lowSearch = search.toLowerCase();
    return wines.filter(wine =>
        keys.some(key => 
            String(wine[key]).toLowerCase().includes(lowSearch) 
        )
    );
}

Call as

filteredWines('Winery 3', ['wineryName', 'wineName']);
Sign up to request clarification or add additional context in comments.

1 Comment

This is awesome thank you! And thanks again @Bert for beautifully vuetifying it for me :)
1

Can also be done this way:

    this.wines = this.wines.filter((item) => {
                return (item.wineryName.toString().toLowerCase().indexOf(val.toLowerCase()) > -1 ||
                item.wineName.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
                item.wineColor.toLowerCase().indexOf(val.toLowerCase()) > -1);
            })

Comments

1

Used the solution from "trincot" and changed it for my angular5 application to this:

filter(search, list): Observable<IFilteredList> {
  return list.filter(item => {
    return Object.values(item).some(val =>
      String(val).includes(search)
    );
  })
}

Comments

0

Filter works. Oops, I read the question a bit more closely. Filter still works but you have to filter the values, too.

let wines = [
    {
        _id: '59a740b8aa06e549918b1fda',
        wineryName: 'Some Winery',
        wineName: 'Pinot Noir',
        wineColor: 'Red',
        imageLink: '/img/FortBerensPN.png'
    },
    {
        _id: '59a7410aaa06e549918b1fdb',
        wineryName: 'Some Winery',
        wineName: 'Pinot Gris',
        wineColor: 'White',
        imageLink: '/img/FortBerensPG.png'
    },
    {
        _id: '59a74125aa06e549918b1fdc',
        wineryName: 'Some Winery',
        wineName: 'Rose',
        wineColor: 'Rose',
        imageLink: '/img/FortBerensRose.png'
    },
    {
        _id: '59a74159aa06e549918b1fdd',
        wineryName: 'Some other Winery',
        wineName: 'Rose',
        wineColor: 'Rose',
        imageLink: '/img/FortBerensRose.png'
    },
    {
        _id: '59a7417aaa06e549918b1fde',
        wineryName: 'Some other Winery',
        wineName: 'Pinot Gris',
        wineColor: 'White',
        imageLink: '/img/FortBerensPG.png'
    },
    {
        _id: '59a8721f4fd43b676a1f5f0d',
        wineryName: 'Some other Winery',
        wineName: 'Pinot Gris',
        wineColor: 'White',
        imageLink: '/img/FortBerensPG.png'
    },
    {
        _id: '59a872244fd43b676a1f5f0e',
        wineryName: 'Winery 3',
        wineName: 'Pinot Noir',
        wineColor: 'Red',
        imageLink: '/img/FortBerensPN.png'
    }
];

let search = (val) => wines.filter(w => Object.values(w).filter(v => v.toLowerCase().indexOf(val.toLowerCase()) !== -1).length > 0);

console.log(search('some'));

Comments

0

I also would recommend trying a more general approach:

function getMatchingWhine(keys, searchTerm, wines) {

  function extractTextFromKeys(keys, object) {
    let text = '';
    keys.forEach(key => {
        text += ' ' + object[key];
    });
    return text.toLowerCase();
  }

  return wines.filter(wine => {
    const relevantText = extractTextFromKeys(keys, wine);
    return relevantText.includes(searchTerm.toLowerCase());
  });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.