[英]ES6 / lodash - check nested Object values with startsWith
有沒有任何功能或任何快速的方法來檢查我們的對象中是否有一些值startsWith as asd
例:
let obj = {
'child' : {
'child_key': 'asdfghhj'
},
'free': 'notasd',
'with': 'asdhaheg'
}
// check here if our obj has value that startsWith('asd')
問候
這是使用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
傳遞的值可以屬於以下三個類別之一:
它等效於null
(也類似於undefined
):在這種情況下,既不能進行遞歸調用,也不能調用startsWith
方法:結果為false
因為此值顯然不是以給定的搜索字符串開頭的;
它是一個對象:在這種情況下,應檢查對象的屬性值。 這將通過遞歸調用來完成。 some
方法可確保一旦找到匹配項,迭代就會停止,並且不會檢查其他屬性值。 在這種情況下, some
返回true
。 如果沒有一個屬性值匹配,則some
返回false
;
以上都不是。 在這種情況下,我們將其startsWith
為字符串(通過應用String
函數),然后在其上應用startsWith
。
在適用步驟中計算出的值將作為函數結果返回。 如果這是遞歸調用,它將在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;
}
如果您真的不在乎匹配哪個節點/值,請使用@trincot的解決方案。 它簡單明了,寫得很好,可以非常有效地解決您的問題。
如果您想要的不僅僅是挖掘布爾值,請繼續閱讀...
我真的懷疑您是否需要這樣做,但是如果您的對象很大,那么您將需要提早退出行為 -這意味着一旦找到匹配項,輸入數據中的迭代就會停止,並且結果為true
/ false
立即返回。 @trincot的解決方案可以提早退出,但是使用map
, filter
或reduce
解決方案則沒有這種行為。
與僅檢查一個字符串值是否以另一個字符串值開頭相比, findDeep
有用得多–它采用了應用於數據中每個葉節點的高階函數。
此答案通過檢查findDeep
返回undefined
(無匹配項),使用我的findDeep
過程來定義通用的anyStartsWith
過程。
它可以使用任何輸入類型,並且可以遍歷Object
和Array
子節點。
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'
您可以遞歸地迭代對象屬性,並使用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'))
您可能會像在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>
更新:另一個可在勻場環境中工作的遞歸對象遍歷器。 這只是一個例子,很容易定制。
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.