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

时间:2016-09-12 18:39:57

标签: javascript object ecmascript-6 lodash

是否有任何函数或任何快速方法来检查我们的对象中的某些值是否开始,例如 asd

示例:

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

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

此致

4 个答案:

答案 0 :(得分:1)

这是一个使用温和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       ) );




说明:

该函数是递归的:它在通过嵌套对象结构时调用自身。传递给obj的值可以属于以下三个类别之一:

  1. 它相当于null(也像undefined):在这种情况下,既不能进行递归调用,也不能调用startsWith方法:结果是false,因为此值显然不是以给定的搜索字符串开头;

  2. 这是一个对象:在这种情况下,应该检查对象的属性值。这将通过递归调用完成。 some方法确保一旦找到匹配,迭代就会停止,并且不会检查其他属性值。在这种情况下,some会返回true。如果没有匹配的属性值,some会返回false;

  3. 以上都不是。在这种情况下,我们将其转换为字符串(通过应用String函数)并在其上应用startsWith

  4. 适用步骤中计算的值将作为功能结果返回。如果这是一个递归调用,它将被视为some回调中的返回值,等等。

    请注意,当您在字符串上调用它时,此函数也会返回正确的结果,如下所示:

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

    非递归替代

    在回答有关潜在堆栈限制的评论时,这里有一个替代的非递归函数,它维护一个"堆栈"在变量中:

    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;
    }
    

答案 1 :(得分:1)

如果您真的不关心匹配的节点/值,请使用@ trincot的解决方案。它非常简单,写得很好,并且可以非常有效地解决您的问题。

如果您想要挖掘的结果不仅仅是布尔值,请继续阅读......

我真的怀疑你需要这个,但是如果你的对象非常大,你会想要一个早期退出行为 - 这意味着一旦找到匹配,迭代你的输入数据将停止,并且会立即返回true / false结果。 @ trincot的解决方案提供早期退出,但使用mapfilterreduce的解决方案不提供此类行为。

findDeep比检查字符串值是否以另一个字符串值开头更有用 - 它需要一个应用于数据中每个叶节点的高阶函数。

此答案使用我的findDeep程序通过检查anyStartsWith是否返回findDeep(不匹配)来定义通用undefined程序

它将适用于任何任何输入类型,它将遍历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




你会发现这有点浪费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'
  }
} 

结果对象有3个属性

  • parents - 匹配值的沿袭中每个节点的完整对象引用
  • path - 获取匹配值的键的路径(堆栈反转)
  • node - 匹配的键/值对

你可以看到,如果我们将父对象作为p并反转路径堆栈,我们将得到匹配的值

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

答案 2 :(得分:0)

您可以递归迭代对象属性,并使用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'))

答案 3 :(得分:0)

您可能会在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>
&#13;
&#13;
&#13;

更新:另一个将在shimmed环境中工作的递归对象walker。这只是一个例子,很容易定制。

&#13;
&#13;
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));
&#13;
<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>
&#13;
&#13;
&#13;