Find Object In Array With Subarray Checking An Property
Solution 1:
Here's how we expect our generic deepFind
function to work
deepFind (x => x.ActionFull === '/budget', bigArray)
// { FunctionalityID: 105, Name: 'Budget', ActionFull: '/budget' ... }
deepFind (x => x.ActionFull === '/budget/allocation', bigArray)
// { FunctionalityID: 106, Name: 'Allocation', ActionFull: '/budget/allocation' }
I'm going to give you the imperative style solution in hopes that this gets you thinking about how you will need to structure your function calls. Notice that this program will begin iterating thru a possible set of solutions but it stops iterating and returns a result as soon as a match is found. Using Array#reduce
or Array#map
or Array#filter
is inadequate for this task as they do not have the short-circuiting behavior we're looking for
Run the program below in your browser, this time with a simplified data set. It's should be easy to follow how we arrive at the result
const data =
[ { a: 1, b: 1 }
, { a: 2, b: 2, c: { d: [ { e: 2 } ] } }
, { a: 3, b: { c: { d: { e: { f: 3 } } } } }
]
constdeepFind = (f, obj = {}) =>
{ if (Object (obj) === obj)
{ if (f (obj) === true)
return obj
for (const [ k, v ] ofObject.entries (obj))
{ const res =
deepFind (f, v)
if (res !== undefined)
return res
}
}
returnundefined
}
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
)
deepFind
works for all objects, arrays included
const alpha =
[ [ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ], [ 'g', 'h', 'i' ] ]
deepFind (x => x [1] === 'h', alpha)
// [ 'g', 'h', 'i', ]
deepFind (([ _0, _1, _2 ]) => _2 === 'f', alpha)
// [ 'd', 'e', 'f' ]
Expressing deepFind
as a functional program requires only the translation of an iterative loop to a recursive function with state parameters. Because you design the recursive function yourself, you can encode the short-circuit behavior this program requires.
Below, deepFind
is written using a pure functional expression. State parameters v
and rest
are added but only f
and o
are meant to be specified by the user, like we did above. If you didn't want these as part your function's public interface, an internal auxiliary function could be used instead.
constidentity = x =>
x
constNone =
Symbol ()
constdeepFind = (f = identity, o = {}, [ _, v ] = [ None, None ], ...rest) =>
Object (o) === o
? f (o) === true
? o
: v === None
? deepFind (f, null, ...Object.entries (o))
: deepFind (f, v, ...rest, ...Object.entries (o))
: v === None
? undefined
: deepFind (f, v, ...rest )
Re-run the program to verify that the output is indeed the same
constidentity = x =>
x
constNone =
Symbol ()
constdeepFind = (f = identity, o = {}, [ _, v ] = [ None, None ], ...rest) =>
Object (o) === o
? f (o) === true
? o
: v === None
? deepFind (f, null, ...Object.entries (o))
: deepFind (f, v, ...rest, ...Object.entries (o))
: v === None
? undefined
: deepFind (f, v, ...rest )
const data =
[ { a: 1, b: 1 }
, { a: 2, b: 2, c: { d: [ { e: 2 } ] } }
, { a: 3, b: { c: { d: { e: { f: 3 } } } } }
]
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
)
const alpha =
[ [ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ], [ 'g', 'h', 'i' ] ]
console.log
( deepFind (x => x [1] === 'h', alpha) // [ 'g', 'h', 'i', ]
, deepFind (([ _0, _1, _2 ]) => _2 === 'f', alpha) // [ 'd', 'e', 'f' ]
)
Lastly, here's a deepFindAll
. In this variation of the program, instead of getting a single answer or undefined
, we get an array of zero or more results. This demonstrates the auxiliary loop I mentioned in the previous example and also a beautiful use case for generators.
As an exercise, I encourage you to rewrite deepFindAll
below using a functional expression in place of the auxiliary generator
constdeepFindAll = (f, o = {}) =>
{ const aux =
function* (f, o)
{ if (Object (o) === o)
{ if (f (o) === true)
yield o
for (const [ _, v ] ofObject.entries (o))
yield* aux (f, v)
}
}
returnArray.from (aux (f, o))
}
const data =
[ { a: 1, b: 1 }
, { a: 2, b: 2, c: { d: [ { e: 2 } ] } }
, { a: 3, b: { c: { d: { e: { f: 3 } } } } }
]
console.log
( deepFindAll (x => x.a === 1 || x.e === 2, data) // [ { a: 1, b: 1 }, { e: 2 } ]
, deepFindAll (x => x.e !== undefined, data) //[ { e: 2 }, { e: { f: 3 } } ]
, deepFindAll (x => x.z === 9, data) // []
)
Solution 2:
Since you have to find the unique item, you can use find
method. The find()
method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
Also, you can use map
method and spread syntax in order to find out all the children elements from the bigArray
elements.
The map()
method creates a new
array with the results of calling a provided callback function on every element in the calling array.
let bigArray = [ { "FunctionalityID": 114, "Name": "General Register", "Action": "/general-register", "Icon": "settings_input_composite", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 1, "FunctionalityChildren": [ { "FunctionalityID": 115, "Name": "Supplier", "Action": "/supplier", "Icon": "perm_contact_calendar", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1251, "Profile_ID": 68, "Functionality_ID": 115, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/supplier" }, { "FunctionalityID": 116, "Name": "RPA", "Action": "/rpa", "Icon": "view_day", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 2, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1252, "Profile_ID": 68, "Functionality_ID": 116, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/rpa" }, { "FunctionalityID": 117, "Name": "Cost Center", "Action": "/cost-center", "Icon": "home", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 3, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1253, "Profile_ID": 68, "Functionality_ID": 117, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/cost-center" }, { "FunctionalityID": 118, "Name": "Departament", "Action": "/departament", "Icon": "donut_small", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 4, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1254, "Profile_ID": 68, "Functionality_ID": 118, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/departament" }, { "FunctionalityID": 119, "Name": "Product Line", "Action": "/product-line", "Icon": "view_headline", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 5, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1255, "Profile_ID": 68, "Functionality_ID": 119, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/product-line" }, { "FunctionalityID": 120, "Name": "Product", "Action": "/product", "Icon": "shopping_cart", "System_ID": 21, "FunctionalityFather_ID": 114, "Active": 1, "Priority": 6, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1256, "Profile_ID": 68, "Functionality_ID": 120, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/general-register/product" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1250, "Profile_ID": 68, "Functionality_ID": 114, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/general-register", "HasFunctionalities": true, "model": false }, { "FunctionalityID": 99, "Name": "Budget Account", "Action": "/budget-account", "Icon": "monetization_on", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 2, "FunctionalityChildren": [ { "FunctionalityID": 100, "Name": "Sector", "Action": "/sector", "Icon": "account_balance", "System_ID": 21, "FunctionalityFather_ID": 99, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1258, "Profile_ID": 68, "Functionality_ID": 100, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget-account/sector" }, { "FunctionalityID": 101, "Name": "Group", "Action": "/group", "Icon": "group_work", "System_ID": 21, "FunctionalityFather_ID": 99, "Active": 1, "Priority": 2, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1259, "Profile_ID": 68, "Functionality_ID": 101, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget-account/group" }, { "FunctionalityID": 102, "Name": "Account", "Action": "/account", "Icon": "attach_money", "System_ID": 21, "FunctionalityFather_ID": 99, "Active": 1, "Priority": 3, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1260, "Profile_ID": 68, "Functionality_ID": 102, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget-account/account" }, { "FunctionalityID": 103, "Name": "Budget", "Action": "/budget", "Icon": "credit_card", "System_ID": 21, "FunctionalityFather_ID": 99, "Active": 1, "Priority": 4, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1261, "Profile_ID": 68, "Functionality_ID": 103, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget-account/budget" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1257, "Profile_ID": 68, "Functionality_ID": 99, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/budget-account", "HasFunctionalities": true, "model": false }, { "FunctionalityID": 105, "Name": "Budget", "Action": "/budget", "Icon": "credit_card", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 3, "FunctionalityChildren": [ { "FunctionalityID": 106, "Name": "Allocation", "Action": "/allocation", "Icon": "note_add", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1272, "Profile_ID": 68, "Functionality_ID": 106, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/allocation" }, { "FunctionalityID": 107, "Name": "Copy", "Action": "/copy", "Icon": "content_copy", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 2, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1273, "Profile_ID": 68, "Functionality_ID": 107, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/copy" }, { "FunctionalityID": 108, "Name": "In And Out", "Action": "/in-and-out", "Icon": "swap_vertical_circle", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 3, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1274, "Profile_ID": 68, "Functionality_ID": 108, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/in-and-out" }, { "FunctionalityID": 109, "Name": "Account Accounting", "Action": "/account-accounting", "Icon": "assignment", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 4, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1275, "Profile_ID": 68, "Functionality_ID": 109, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/account-accounting" }, { "FunctionalityID": 110, "Name": "Event", "Action": "/eventos", "Icon": "shopping_cart", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 5, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1276, "Profile_ID": 68, "Functionality_ID": 110, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/eventos" }, { "FunctionalityID": 111, "Name": "Copy Counter", "Action": "/copy-counter", "Icon": "swap_vertical_circle", "System_ID": 21, "FunctionalityFather_ID": 105, "Active": 1, "Priority": 6, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1277, "Profile_ID": 68, "Functionality_ID": 111, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/budget/copy-counter" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1271, "Profile_ID": 68, "Functionality_ID": 105, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/budget", "HasFunctionalities": false, "model": false }, { "FunctionalityID": 112, "Name": "Config", "Action": "/config", "Icon": "build", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 4, "FunctionalityChildren": [ { "FunctionalityID": 113, "Name": "Control Year Month", "Action": "/control-year-month", "Icon": "date_range", "System_ID": 21, "FunctionalityFather_ID": 112, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1263, "Profile_ID": 68, "Functionality_ID": 113, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/config/control-year-month" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1262, "Profile_ID": 68, "Functionality_ID": 112, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/config", "HasFunctionalities": true, "model": false }, { "FunctionalityID": 121, "Name": "Report", "Action": "/report", "Icon": "picture_as_pdf", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 5, "FunctionalityChildren": [ { "FunctionalityID": 122, "Name": "Report 1", "Action": "/report-um", "Icon": "picture_as_pdf", "System_ID": 21, "FunctionalityFather_ID": 121, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1265, "Profile_ID": 68, "Functionality_ID": 122, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/report/report-um" }, { "FunctionalityID": 123, "Name": "Report 2", "Action": "/report-dois", "Icon": "picture_as_pdf", "System_ID": 21, "FunctionalityFather_ID": 121, "Active": 1, "Priority": 2, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1266, "Profile_ID": 68, "Functionality_ID": 123, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/report/report-dois" }, { "FunctionalityID": 124, "Name": "Report 3", "Action": "/report-tres", "Icon": "picture_as_pdf", "System_ID": 21, "FunctionalityFather_ID": 121, "Active": 1, "Priority": 3, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1267, "Profile_ID": 68, "Functionality_ID": 124, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/report/report-tres" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1264, "Profile_ID": 68, "Functionality_ID": 121, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/report", "HasFunctionalities": true, "model": false }, { "FunctionalityID": 125, "Name": "Profile", "Action": "/profile", "Icon": "person", "System_ID": 21, "FunctionalityFather_ID": null, "Active": 1, "Priority": 6, "FunctionalityChildren": [ { "FunctionalityID": 126, "Name": "New", "Action": "/new", "Icon": "plus_one", "System_ID": 21, "FunctionalityFather_ID": 125, "Active": 1, "Priority": 1, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1269, "Profile_ID": 68, "Functionality_ID": 126, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/profile/new" }, { "FunctionalityID": 127, "Name": "List", "Action": "/list", "Icon": "view_list", "System_ID": 21, "FunctionalityFather_ID": 125, "Active": 1, "Priority": 2, "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1270, "Profile_ID": 68, "Functionality_ID": 127, "CanInsert": true, "CanUpdate": true, "CanDelete": true } ], "ActionFull": "/profile/list" } ], "ProfileFunctionalities": [ { "ProfileFunctionalityID": 1268, "Profile_ID": 68, "Functionality_ID": 125, "CanInsert": false, "CanUpdate": false, "CanDelete": false } ], "ActionFull": "/profile", "HasFunctionalities": true, "model": false } ]
let action_full = '/budget/allocation';
let result = [].concat(...bigArray.map(elem => elem.FunctionalityChildren))
.find(a => a.ActionFull == action_full); // It work only to children objects.console.log(result);
let findedInFatherAndChild = bigArray.concat(...bigArray.map(elem => elem.FunctionalityChildren)).find(a => a.ActionFull === '/budget') // This work for me, both for father and children. Thanks Mihai
Post a Comment for "Find Object In Array With Subarray Checking An Property"