简体   繁体   English

ES6 / lodash-使用startsWith检查嵌套的对象值

[英]ES6 / lodash - check nested Object values with startsWith

is there any function or any fast way to check if some value in our object startsWith eg asd 有没有任何功能或任何快速的方法来检查我们的对象中是否有一些值startsWith as asd

Example: 例:

let obj = { 
   'child' : {
      'child_key': 'asdfghhj'
    },
    'free': 'notasd',
    'with': 'asdhaheg'
  }

// check here if our obj has value that startsWith('asd')

Regards 问候

Here is a function with mild ES6 usage: 这是使用ES6的函数:

 function startsWithRecursive(obj, needle) { return obj != null && (typeof obj === "object" ? Object.keys(obj).some( key => startsWithRecursive(obj[key], needle) ) : String(obj).startsWith(needle)); } // Sample data let obj = { 'child' : { 'child_key': 'asdfghhj' }, 'free': 'notasd', 'with': 'asdhaheg' }; // Requests console.log( 'obj, "asd":', startsWithRecursive(obj, 'asd' ) ); console.log( 'obj, "hello":', startsWithRecursive(obj, 'hello' ) ); console.log( 'null, "":', startsWithRecursive(null, '' ) ); console.log( 'undefined, "":', startsWithRecursive(undefined, '' ) ); console.log( '"test", "te":', startsWithRecursive('test', 'te' ) ); console.log( '12.5, 1:', startsWithRecursive(12.5, 1 ) ); 

Explanation: 说明:

The function is recursive: it calls itself as it goes through a nested object structure. 该函数是递归的:它在通过嵌套对象结构时进行调用。 The value passed as obj can fall in one of the following three categories: 作为obj传递的值可以属于以下三个类别之一:

  1. It is equivalent to null (like also undefined ): in that case neither a recursive call, nor a call of the startsWith method can be made: the result is false as this value obviously does not start with the given search string; 它等效于null (也类似于undefined ):在这种情况下,既不能进行递归调用,也不能调用startsWith方法:结果为false因为此值显然不是以给定的搜索字符串开头的;

  2. It is an object: in that case that object's property values should be inspected. 它是一个对象:在这种情况下,应检查对象的属性值。 This will be done through recursive calls. 这将通过递归调用来完成。 The some method makes sure that as soon a match has been found, the iteration stops, and no further property values are inspected. some方法可确保一旦找到匹配项,迭代就会停止,并且不会检查其他属性值。 In that case some returns true . 在这种情况下, some返回true If none of the property values matched, some returns false ; 如果没有一个属性值匹配,则some返回false

  3. It is none of the above. 以上都不是。 In that case we cast it to string (by applying the String function) and apply startsWith on it. 在这种情况下,我们将其startsWith为字符串(通过应用String函数),然后在其上应用startsWith

The value calculated in the applicable step will be returned as function result. 在适用步骤中计算出的值将作为函数结果返回。 If this was a recursive call, it will be treated as return value in the some callback, ...etc. 如果这是递归调用,它将在some回调中被视为返回值,等等。

Note that this function also returns the correct result when you call it on a string, like so: 请注意,当您在字符串上调用该函数时,它还会返回正确的结果,如下所示:

startsWithRecursive('test', 'te'); // true

Non-Recursive Alternative 非递归替代

In answer to comments about potential stack limitations, here is an alternative non-recursive function which maintains a "stack" in a variable: 为了回答有关潜在堆栈限制的评论,以下是一个可选的非递归函数,该函数在变量中维护“堆栈”:

function startsWithRecursive(obj, needle) {
    var stack = [obj];
    while (stack.length) {
        obj = stack.pop();
        if (obj != null) {
            if (typeof obj === "object") {
                stack = stack.concat(Object.keys(obj).map( key => obj[key] ));
            } else {
                if (String(obj).startsWith(needle)) return true;
            }
        }
    }
    return false;
}

Use @trincot's solution if you really don't care about which node/value matched. 如果您真的不在乎匹配哪个节点/值,请使用@trincot的解决方案。 It's straightforward, well-written, and solves your problem very effectively. 它简单明了,写得很好,可以非常有效地解决您的问题。

If you want more than just a Boolean value as the result of your digging, read along ... 如果您想要的不仅仅是挖掘布尔值,请继续阅读...


I really doubt your need for this, but if your object is significantly large, you will want an early exit behaviour – what this means is that as soon as a match is found, iteration through your input data will stop and true / false result will be returned immediately. 我真的怀疑您是否需要这样做,但是如果您的对象很大,那么您将需要提早退出行为 -这意味着一旦找到匹配项,输入数据中的迭代就会停止,并且结果为true / false立即返回。 @trincot's solution offers early exit, but solutions that using map , filter , or reduce offer no such behaviour. @trincot的解决方案可以提早退出,但是使用mapfilterreduce解决方案则没有这种行为。

findDeep is much more useful than just checking if a string value starts with another string value – it takes a higher-order function that is applied for each leaf node in your data. 与仅检查一个字符串值是否以另一个字符串值开头相比, findDeep有用得多–它采用了应用于数据中每个叶节点的高阶函数。

This answer uses my findDeep procedure to define a generic anyStartsWith procedure by checking if findDeep returns undefined (no match) 此答案通过检查findDeep返回undefined (无匹配项),使用我的findDeep过程来定义通用的anyStartsWith过程。

It will work any any input type and it will traverse Object and Array child nodes. 它可以使用任何输入类型,并且可以遍历ObjectArray子节点。

 const isObject = x=> Object(x) === x const isArray = Array.isArray const keys = Object.keys const rest = ([x,...xs]) => xs const findDeep = f => x => { let make = (x,ks)=> ({node: x, keys: ks || keys(x)}) let processNode = (parents, path, {node, keys:[k,...ks]})=> { if (k === undefined) return loop(parents, rest(path)) else if (isArray(node[k]) || isObject(node[k])) return loop([make(node[k]), make(node, ks), ...parents], [k, ...path]) else if (f(node[k], k)) return {parents, path: [k,...path], node} else return loop([{node, keys: ks}, ...parents], path) } let loop = ([node,...parents], path) => { if (node === undefined) return undefined else return processNode(parents, path, node) } return loop([make(x)], []) } const startsWith = x => y => y.indexOf(x) === 0 const anyStartsWith = x => xs => findDeep (startsWith(x)) (xs) !== undefined let obj = { 'child' : { 'child_key': 'asdfghhj' }, 'free': 'notasd', 'with': 'asdhaheg' } console.log(anyStartsWith ('asd') (obj)) // true console.log(anyStartsWith ('candy') (obj)) // false 


You'll see this is kind of a waste of findDeep 's potential, but if you don't need it's power then it's not for you. 您会看到,这有点浪费了findDeep的潜力,但如果您不需要它的强大功能,那么它就不适合您。

Here's the real power of findDeep 这是findDeep的真正力量

findDeep (startsWith('asd')) (obj)

// =>     
{
  parents: [
    {
      node: {
        child: {
          child_key: 'asdfghhj'
        },
        free: 'notasd',
        with: 'asdhaheg'
      },
      keys: [ 'free', 'with' ]
    }
  ],
  path: [ 'child_key', 'child' ],
  node: {
    child_key: 'asdfghhj'
  }
} 

The resulting object has 3 properties 生成的对象具有3个属性

  • parents – the full object reference to each node in the matched value's lineage parents –匹配值沿袭中对每个节点的完整对象引用
  • path – the path of keys to get to the matched value (stack reversed) path –达到匹配值的键的路径(堆栈反向)
  • node – the key/value pair that matched node –匹配的键/值对

You can see that if we take the parent object as p and reverse the path stack, we get to the matched value 您可以看到,如果我们将父对象作为p并反转路径堆栈,则可以得到匹配的值

p['child']['child_key']; //=> 'asdfghhj'

You can recursively iterate object properties and check if property starts with prefix using find function: 您可以递归地迭代对象属性,并使用find函数检查属性是否以prefix开头:

function hasPropertyStartingWith(obj, prefix) {
  return !!Object.keys(obj).find(key => {
    if (typeof obj[key] === 'object') {
      return hasPropertyStartingWith(obj[key], prefix)
    }
    if (typeof obj[key] === 'string') {
      return obj[key].startsWith(prefix)
    }
    return false
  })
}

console.log(hasPropertyStartingWith(obj, 'asd'))

You may get away with something as simple as using a RegExp on a JSON string, something like 您可能会像在JSON字符串上使用RegExp这样简单的事情,例如

 var obj = { 'child': { 'child_key': 'asdfghhj' }, 'free': 'notasd', 'with': 'asdhaheg' }; function customStartsWith(obj, prefix) { return new RegExp(':"' + prefix + '[\\\\s\\\\S]*?"').test(JSON.stringify(obj)); } console.log('obj, "asd":', customStartsWith(obj, 'asd')); console.log('obj, "hello":', customStartsWith(obj, 'hello')); console.log('null, "":', customStartsWith(null, '')); console.log('undefined, "":', customStartsWith(undefined, '')); console.log('"test", "te":', customStartsWith('test', 'te')); console.log('12.5, 1:', customStartsWith(12.5, 1)); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.js"></script> 

Update: Another recursive object walker that will work in a shimmed environment. 更新:另一个可在匀场环境中工作的递归对象遍历器。 This is just an example and it is easily customised. 这只是一个例子,很容易定制。

 var walk = returnExports; var obj = { 'child': { 'child_key': 'asdfghhj' }, 'free': 'notasd', 'with': 'asdhaheg' }; function customStartsWith(obj, prefix) { var found = false; walk(obj, Object.keys, function(value) { if (typeof value === 'string' && value.startsWith(prefix)) { found = true; walk.BREAK; } }); return found; } console.log('obj, "asd":', customStartsWith(obj, 'asd')); console.log('obj, "hello":', customStartsWith(obj, 'hello')); console.log('null, "":', customStartsWith(null, '')); console.log('undefined, "":', customStartsWith(undefined, '')); console.log('"test", "te":', customStartsWith('test', 'te')); console.log('12.5, 1:', customStartsWith(12.5, 1)); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.js"></script> <script src="https://rawgithub.com/Xotic750/object-walk-x/master/lib/object-walk-x.js"></script> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM