javascript - In an array of objects, returns objects where ANY value matches a specific string

后端 未结 5 803
清酒与你
清酒与你 2020-12-04 00:56

I\'m implementing search functionality into my application. The search results in the UI are returned based on an array of objects. Essentially what I\'m trying to do is ite

相关标签:
5条回答
  • 2020-12-04 01:13
    function searchObj(search){
      let answer = [];
      result.forEach(re => {
        if(JSON.stringify(re).indexOf(search) > 0){
          answer.push(re)
        }
      });
      return answer;
    }
    

    Loop through every element of the array, convert them into a string and use indexOf to find the matching criteria. That way you can save a few loops without looping each and every key of every element.

    0 讨论(0)
  • 2020-12-04 01:16

    recursive search

    This is a topic I've written about recently. Here is a generic deepFind. It works recursively and can "search" any input value.

    Below we construct a simple set of data and then show how deepFind can search the data and return matches

    const data =
      [ { a: 1, b: 1 }
      , { a: 2, b: 2, c: { d: [ { e: 2 } ] } }
      , { a: 3, b: { c: { d: { e: { f: 3 } } } } }
      ]
    
    const deepFind = (f, obj = {}) =>
    { if (Object (obj) === obj)
      { if (f (obj) === true)
          return obj
    
        for (const [ k, v ] of Object.entries (obj))
        { const res =
            deepFind (f, v)
    
          if (res !== undefined)
            return res
        }
      }
    
      return undefined
    }
    
    console.log
      ( deepFind (x => x.a === 1, data)             // { a: 1, b: 1 }
      , deepFind (x => x.e === 2, data)             // { e: 2 }
      , deepFind (x => Array.isArray(x.d), data)    // { d: [ { e: 2 } ] }
      , deepFind (x => x.f === 3, data)             // { f: 3 }
      , deepFind (x => x.e && x.e.f === 3, data)    // { e: { f: 3 } }
      , deepFind (x => x.z === 9, data)             // undefined
      )

    Above deepFind only works by matching values directly using ===. Because it accepts a higher-order function f however, we can specialize its behavior in meaningful ways.

    string match using deepFind

    Below we encode our generic string-matching search function using deepFind

    const search = (query = "", data) =>
      deepFind
        ( o =>
            Object.values (o) .some (v =>
              String (v) === v && v .includes (query))
        , data
        )
    
    search ("D", result)
    // { name: "Donna Shomaker", ... }
    
    search ("Du", result)
    // { name: "Ron Duluth", ... }
    
    search ("ng3", result)
    // { name: "Jimmy Dawson", sneak: "string3 string3 string3", ... }
    
    search ("zzz", result)
    // undefined
    

    Verify the results in your own browser

    const deepFind = (f, obj = {}) =>
    { if (Object (obj) === obj)
      { if (f (obj) === true)
          return obj
    
        for (const [ k, v ] of Object.entries (obj))
        { const res =
            deepFind (f, v)
          
          if (res !== undefined)
            return res
        }
      }
    
      return undefined
    }
    
    const search = (query = "", data) =>
      deepFind
        ( o =>
            Object.values (o) .some (v =>
              String (v) === v && v .includes (query))
        , data
        )
    
    const result =
      [ { name: 'Donna Shomaker'
        , custNumber: '6658924351'
        , sneak: 'string1 string1 string1'
        , foo: false
        , bar: false
        }
      , { name: 'Ron Duluth'
        , custNumber: '8812654434'
        , sneak: 'string2 string2 string2'
        , foo: false
        , bar: false
        }
      , { name: 'Jimmy Dawson'
        , custNumber: '8908198230'
        , sneak: 'string3 string3 string3'
        , foo: false
        , bar: false
        }
      ]
    
    console.log (search ("D", result))
    // { name: "Donna Shomaker", ... }
    
    console.log (search ("Du", result))
    // { name: "Ron Duluth", ... }
    
    console.log (search ("ng3", result))
    // { name: "Jimmy Dawson", sneak: "string3 string3 string3", ... }
    
    console.log (search ("zzz", result))
    // undefined

    returning multiple search results

    The program above only returns the first match. If you wanted to return all of the results, we can do so using generators, which are perfectly suited for this task

    const deepFindAll = function* (f, o = {})
    { if (Object (o) === o)
      { if (f (o) === true)
          yield o
        for (const [ _, v ] of Object.entries (o))
          yield* deepFindAll (f, v)
      }
    }
    

    Now we implement searchAll using our new generator

    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( o =>
                Object.values (o) .some (v =>
                  String (v) === v && v .includes (query))
            , data
            )
        )
    
    searchAll ("81", result)
    // [ { custNumber: '8812654434', ... }
    // , { custNumber: '8908198230', ... }
    // ]
    
    searchAll ("Du", result)
    // [ { name: "Ron Duluth", ... } ]
    
    searchAll ("zzz", result)
    // []
    

    Run searchAll in your browser below

    const deepFindAll = function* (f, o = {})
    { if (Object (o) === o)
      { if (f (o) === true)
          yield o
        for (const [ _, v ] of Object.entries (o))
          yield* deepFindAll (f, v)
      }
    }
    
    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( o =>
                Object.values (o) .some (v =>
                  String (v) === v && v .includes (query))
            , data
            )
        )
    
    const result =
      [ { name: 'Donna Shomaker'
        , custNumber: '6658924351'
        , sneak: 'string1 string1 string1'
        , foo: false
        , bar: false
        }
      , { name: 'Ron Duluth'
        , custNumber: '8812654434'
        , sneak: 'string2 string2 string2'
        , foo: false
        , bar: false
        }
      , { name: 'Jimmy Dawson'
        , custNumber: '8908198230'
        , sneak: 'string3 string3 string3'
        , foo: false
        , bar: false
        }
      ]
    
    console.log (searchAll ("81", result))
    // [ { custNumber: '8812654434', ... }
    // , { custNumber: '8908198230', ... }
    // ]
    
    console.log (searchAll ("Du", result))
    // [ { name: "Ron Duluth", ... } ]
    
    console.log (searchAll ("zzz", result))
    // []

    case insensitive search

    Above, our search function uses v .includes (query) but because we're working with a higher-order function, we can make the behaviour as specific as we want.

    Using searchAll as an example, we could change it like below

    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( o =>
                Object.values (o) .some (v =>
                  String (v) === v && v .includes (query))
                  String (v) === v
                    && v .toLowerCase () .includes (query .toLowerCase ()))
            , data
            )
        )

    But that's making a complete mess of our function. It's time to abstract a little more and explain what we're doing by giving our intentions names

    const anyString = f => o =>
      Object.values (o) .some (v =>
        String (v) === v && f (v))
    
    const caseInsenstiveMatch = (x, y) =>
      x.toLowerCase () .includes (y.toLowerCase ())
    
    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( anyString (v =>
                caseInsenstiveMatch (v, query))
            , data
            )
        )

    Isolating behaviors and defining separate functions is an important part of writing good programs. Showing search and searchAll side-by-side highlights this importance. The new helpers anyString and caseInsensitiveSearch keep the code clear, but also make it easier to reuse behaviours where needed.

    const search = (query = "", data) =>
      deepFind
        ( anyString (v =>
            caseInsenstiveMatch (v, query))
        , data
        )
    
    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( anyString (v =>
                caseInsenstiveMatch (v, query))
            , data
            )
        )
    

    contramap

    Higher-order functions have all sorts of uses for keeping our code clean and descriptive. Below, we define dead-simple versions of match and lower. Then using contramap, we bring our program together.

    The emphasis here is on the simplicity of each function. A simple function is easier to test, easier to debug, and easier to combine with other simple functions. The Unix philosophy, "Do one thing and do it well" should be ringing in your ears right now

    const contramap = (f, g) =>
      (x, y) => f (g (x), g (y))
    
    const match = (x = "", y = "") =>
      x .includes (y)
    
    const lower = (x = "") =>
      x .toLowerCase ()
    
    const caseInsenstiveMatch =
      contramap (match, lower)
    
    const anyString = (f) => (o = {}) =>
      Object.values (o) .some (v =>
        String (v) === v && f (v))
    
    const searchAll = (query = "", data = {}) =>
      Array.from
        ( deepFindAll
            ( anyString (v =>
                caseInsenstiveMatch (v, query))
            , data
            )
        )

    Contramap unlocks other powers that may not be immediately obvious. If it interests you, I recommend Monoidal Contravariant Functors are actually useful! by Brian Lonsdorf. Don't let the title scare you; the author has a knack for making this stuff easy.

    0 讨论(0)
  • 2020-12-04 01:17

    A 'some' in your filter might do the trick, checking all the keys.

    return result.filter(convo => {
      return Object.keys(convo).some(key => {
         return convo[key].toLowerCase().includes(searchbarVal.toLowerCase())
      })
    })
    
    0 讨论(0)
  • 2020-12-04 01:21

    Try

    let search= result.filter(x=> ['name','custNumber','sneak']
         .reduce((o,a)=> x[a].toLowerCase().includes(query.toLowerCase())||o, false) );
    

    Where query is your searchbarVal.toLowerCase()

    var result = [{
      name: 'Donna Shomaker',
      custNumber: '6658924351',
      sneak: 'string1 string1 string1',
      foo: false,
      bar: false,
    },
    {
      name: 'Ron Duluth',
      custNumber: '8812654434',
      sneak: 'string2 string2 string2',
      foo: false,
      bar: false,
    },
    {
      name: 'Jimmy Dawson',
      custNumber: '8908198230',
      sneak: 'string3 string3 string3',
      foo: false,
      bar: false,
    }
    ]
    
    let query="89"; // searchbarVal.toLowerCase()
    
    let search= result.filter(x=> ['name','custNumber','sneak'].reduce((o,a)=> x[a].toLowerCase().includes(query.toLowerCase())||o, false) );
    
    console.log(search);

    0 讨论(0)
  • 2020-12-04 01:29

    You can loop through the object and try and do something like the following:

    var result = [{
      name: 'Donna Shomaker',
      custNumber: '6658924351',
      sneak: 'string1 string1 string1',
      foo: false,
      bar: false,
    },
    {
      name: 'Ron Duluth',
      custNumber: '8812654434',
      sneak: 'string2 string2 string2',
      foo: false,
      bar: false,
    },
    {
      name: 'Jimmy Dawson',
      custNumber: '8908198230',
      sneak: 'string3 string3 string3',
      foo: false,
      bar: false,
    }
    ];
    
    var searchStr = "Donna";
    
    console.log(searchObj(searchStr));
    
    function searchObj(search){
      var searchResult = [];
      for(var obj in result){
          var str = JSON.stringify(result[obj]);
          if(str.indexOf(search) > 0){
            searchResult.push(result[obj]);
          }
      }
      return searchResult;
    }

    0 讨论(0)
提交回复
热议问题