admin管理员组文章数量:1357646
How to filter an object given its partial path?
As an example.
let address = {
country :{
name:'Japan',
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
},
code:'JP'
},
nearbyCountry:'Korea'
}
path1: countr.cit
For address
, path1 will result in
{
country :{
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
}
}
}
path2: countr
For path2,
I should get entire address
object because countr is present in country
and nearbyCountry
{
country :{
name:'Japan',
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
}
},
nearbyCountry:'Korea'
}
Edit: I have been able to solve this when given an exact path (ex: country.city
). But having difficulty with partial paths.
How to filter an object given its partial path?
As an example.
let address = {
country :{
name:'Japan',
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
},
code:'JP'
},
nearbyCountry:'Korea'
}
path1: countr.cit
For address
, path1 will result in
{
country :{
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
}
}
}
path2: countr
For path2,
I should get entire address
object because countr is present in country
and nearbyCountry
{
country :{
name:'Japan',
city:{
name:'Tokyo',
town: {
name:'korushawa'
}
}
},
nearbyCountry:'Korea'
}
Edit: I have been able to solve this when given an exact path (ex: country.city
). But having difficulty with partial paths.
- Can you add the solution you have for full paths? – Steven B. Commented Apr 11, 2019 at 5:09
- Its actually very straightforward. Check this – asdasd Commented Apr 11, 2019 at 5:10
3 Answers
Reset to default 4 +50This approach rely on filtering the entries and rebuilding a new object by mapping objects with a single property.
function getParts(object, fragments) {
var [part, ...rest] = fragments.split('.');
return Object.assign({}, ...Object
.entries(object)
.filter(([key]) => key.toLowerCase().includes(part))
.map(([k, v]) => {
if (!rest.length) return { [k]: v };
var parts = v && typeof v === 'object' && getParts(v, rest.join('.'));
if (parts) return { [k]: parts };
})
);
}
let address = { country: { name: 'Japan', city: { name: 'Tokyo', town: { name: 'korushawa' } }, code: 'JP' }, nearbyCountry: 'Korea' };
console.log(getParts(address, 'countr.cit'));
console.log(getParts(address, 'countr'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Since I don't fully understand your goal (for example: why on your first output example do you keep name
property of country
object but not the code
property), I will give you two approachs that I hope can help you.
First approach:
In this first approach we recursively traverse the main object to filter out the keys
that don't match at a particular level. The output will be an object
with the properties that match at a specific level:
let address = {
country: {
name: 'Japan',
city: {name: 'Tokyo', town: {name: 'korushawa'}},
code: 'JP'
},
nearbyCountry: {name: 'Korea', code: 'KO'}
};
const myFilter = (obj, keys) =>
{
if (keys.length <= 0)
return obj;
return Object.keys(obj).reduce(
(nObj, k) => k.toLowerCase().match(keys[0])
? ({...nObj, [k]: myFilter(obj[k], keys.slice(1))})
: nObj,
{}
);
}
const customFilter = (o, k) => myFilter(
JSON.parse(JSON.stringify(o)),
k.split(".").map(x => x.toLowerCase())
);
console.log("Filter by 'countr':", customFilter(address, "countr"));
console.log("Filter by 'countr.cit':", customFilter(address, "countr.cit"));
console.log("Filter by 'countr.cit.to':", customFilter(address, "countr.cit.to"));
console.log("Filter by 'countr.cit.lala':", customFilter(address, "countr.cit.lala"));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
As you can see, when filtering by "countr.cit"
this approach keeps the key = nearbyCountry
even if there isn't an inner key
matching with cit
inside of it.
Second approach
In this approach we going to filter out all the first-level keys
of the main object
that don't have a match on all the sections of the provided path
. However, I have to say that this approach is a little strange. I believe this will have more sense if your input is an array of objects, not just one object.
let address = {
country: {
name: 'Japan',
city: {name: 'Tokyo', town: {name: 'korushawa'}},
code: 'JP'
},
nearbyCountry: {name: 'Korea', code: 'KO'}
};
const myFilter = (obj, paths) =>
{
let newObj = {};
Object.entries(obj).forEach(([key, val]) =>
{
let res = paths.slice(1).reduce((o, cp) =>
{
if (o === undefined) return o;
let found = Object.keys(o).find(k => k.toLowerCase().match(cp));
return found !== undefined ? o[found] : found;
}, val);
if (key.toLowerCase().match(paths[0]) && res !== undefined)
newObj[key] = val;
});
return newObj;
}
const customFilter = (o, k) => myFilter(
JSON.parse(JSON.stringify(o)),
k.split(".").map(x => x.toLowerCase())
);
console.log("Filter by 'countr':", customFilter(address, "countr"));
console.log("Filter by 'countr.cit':", customFilter(address, "countr.cit"));
console.log("Filter by 'countr.cit.to':", customFilter(address, "countr.cit.to"));
console.log("Filter by 'countr.cit.lala':", customFilter(address, "countr.cit.lala"));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Finally, the other thing you may want to do is already shown on the answer provided by @adiga.
- You could create a recursive function which takes an object and an array of partial paths as a parameter.
- Get the latest partial path and
filter
the keys whichinclude
that path - If there are no keys, return null
- Else, create an object from the keys using
reduce
- if there are no more keys left, just add the all the filtered properties of
obj
to the accumulator - else, recursively call the function get another nested object
- if there are no more keys left, just add the all the filtered properties of
const address={country:{name:'Japan',city:{name:'Tokyo',town:{name:'korushawa'}},code:'JP'},nearbyCountry:'Korea'};
function filterObject(obj, paths) {
if (!obj) return null;
const partial = paths.shift(),
filteredKeys = Object.keys(obj).filter(k => k.toLowerCase().includes(partial));
if (!filteredKeys.length) return null; // no keys with the path found
return filteredKeys.reduce((acc, key) => {
if(!paths.length) return { ...acc, [key]: obj[key] }
const nest = filterObject(obj[key], [...paths]) // filter another level
return nest ? { ...acc, [key]: nest } : acc
}, null)
}
let path;
console.log(path = 'countr', ':');
console.log(filterObject(address, path.split('.')))
console.log(path = 'countr.cit', ':');
console.log(filterObject(address, path.split('.')))
console.log(path = 'countr.cit.to', ':');
console.log(filterObject(address, path.split('.')))
console.log(path = 'countr.cit.doesntexist', ':');
console.log(filterObject(address, path.split('.')))
.as-console-wrapper {max-height:100% !important; top:0;}
If you just need the first key that fully or partial matches the keys, you could split the path
and use reduce
like this. If the key is found, return the object, else find the keys which include
the given key (This gives the data for the last key match. Not the entire object tree)
const address={country:{name:'Japan',city:{name:'Tokyo',town:{name:'korushawa'}},code:'JP'},nearbyCountry:'Korea'},
path = "countr.cit";
const output = path.split('.').reduce((obj, key) => {
if (key in obj) {
return obj[key];
} else {
let found = Object.keys(obj).find(k => k.includes(key));
if (found)
return obj[found]
else
return {}
}
}, address);
console.log(output)
本文标签: javascriptFilter an object by partial pathsStack Overflow
版权声明:本文标题:javascript - Filter an object by partial paths - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744019325a2576925.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论