#javascript
#javascript
Вопрос:
Представьте, что есть объект, и мы пытаемся написать функцию, которая принимает ‘path’ в качестве параметра и печатает все, что находится внутри него. Если входные данные неверны, выдайте ошибку. Размер объекта может быть огромным.
const obj = {
test: {
demo: [{
lname: 'dave'
}]
}
};
function getData(obj, dest) {
const path = dest.split('.');
return helper(obj, path);
function helper(obj, path) {
if (!path.length) return obj;
const cur = path.shift();
if ((Array.isArray(obj) amp;amp; typeof obj === 'string') ||
(typeof obj === 'undefined')) {
throw new Error("Something wrong")
}
obj = obj[cur];
return helper(obj, path);
}
}
console.log(getData(obj, 'test.demo.0.lname'));
//console.log(getData(obj, 'test.demo.dave.lname')); // throws an error since in demo array you can't access 'dave'
Я пытаюсь выяснить, каковы более короткие способы написания этого метода? Я слышал, как кто-то сказал, что мы можем написать это в паре строк.
Комментарии:
1. Вы могли бы написать это в паре строк, но вам пришлось бы пропустить проверки, которые вы выполняете в данный момент, поэтому запуск
getData({a: 1}, "a.b"
)` может завершиться ошибкой по-разному.2. Конечно, но можете ли вы дать мне сокращенную версию?
3. Хорошо, я дам вам ответ. Однако, один момент — когда это условие будет выполнено?
(Array.isArray(obj) amp;amp; typeof obj==='string')
4.
Array.isArray(obj) amp;amp; typeof obj==='string'
Это условие никогда не будет выполненоtrue
, потому чтоArray.isArray
в основном эквивалентноObject.prototype.toString.call(obj) === "[object Array]"
, где строка выдаст"[object String]"
5. @VLAZ что, если мой ввод
console.log(getData(obj, 'test.demo.dave.lname'));
тогда он сломается
Ответ №1:
Простая и короткая реализация может использовать Array#reduce для итеративного извлечения ключей из объекта следующим образом:
const obj = {
test: {
demo: [{
lname: 'dave'
}]
}
};
function getData(obj, dest) {
var keys = dest.split(".");
return keys.reduce(function(currentObject, key) {
if(typeof currentObject == "undefined") throw Error("Something wrong");
return currentObject[key];
}, obj)
}
console.log(getData(obj, 'test.demo.0.lname'));
console.log(getData(obj, 'test.demo.dave.lname')); // throws an error since in demo array you can't access 'dave'
Это более подробный вариант, чтобы продемонстрировать, что происходит, вы можете сократить его еще больше
const obj = {
test: {
demo: [{
lname: 'dave'
}]
}
};
function getData(obj, dest) {
return dest.split(".").reduce((curr, key) => curr[key], obj)
}
console.log(getData(obj, 'test.demo.0.lname'));
console.log(getData(obj, 'test.demo.dave.lname')); // throws an error since in demo array you can't access 'dave'
Вы также можете избежать ошибок, а просто вернуть undefined
, если ключ не найден
const obj = {
test: {
demo: [{
lname: 'dave'
}]
}
};
function getData(obj, dest) {
return dest.split(".").reduce((curr, key) => curr != undefined ? curr[key] : undefined, obj)
}
console.log(getData(obj, 'test.demo.0.lname'));
console.log(getData(obj, 'test.demo.dave.lname')); // undefined
Однако, хотя это просто реализовать, это связано с проверкой ошибок. Отладка того, что именно пошло не так, может быть действительно раздражающей, поскольку вы должны знать, что такое объект данных и входные данные назначения, а затем попытаться выяснить, какой ключ отсутствовал вручную. Таким образом, более длительная реализация обычно лучше, если вы хотите более надежный и гибкий код.
Если вы используете Lodash, то вы можете использовать их _.get, который еще более надежен и обрабатывает больше синтаксиса
const obj = {
test: {
demo: [{
lname: 'dave'
}]
}
};
console.log(_.get(obj, 'test.demo.0.lname'));
console.log(_.get(obj, 'test.demo[0].lname'));
console.log(_.get(obj, ['test', 'demo', 0, 'lname']));
console.log(_.get(obj, 'test.demo.dave.lname')); // undefined
console.log(_.get(obj, 'test.demo.dave.lname', 'this is not dave but the default vale'));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
Комментарии:
1. Почему это не удается для ` const obj2 = { keys: { lists: [{ FirstName: ‘dave’ }] } }; `
2. Какой ключ вы пытаетесь получить от него? И видите — это проблема с отладкой, о которой я упоминал.
3. Я пытаюсь получить
obj2.keys.lists.0.firstName
4. Это работает … если вы вызываете его как
getData(obj2, "keys.lists.0.firstName")
— если вы попыталисьgetData(obj2, "obj2.keys.lists.0.firstName")
, то это не сработает, потому что нетobj2
ключа.
Ответ №2:
Если можно использовать Lodash, то:
if(!_.has(obj,path)){
//bad
}else{
let val = _.get(obj,path);
}
Если вас не волнует, существует ли какой-либо путь или он имеет undefined
значение, тогда вы можете просто сделать:
let val = _.get(obj,path);
if(val===undefined){
//bad
}
Если вас волнуют разреженные массивы и «пустые слоты», то не полагайтесь на _.has
метод, потому что он возвращает true
пустой слот.
Вместо этого вы можете использовать альтернативный _.exists
метод из расширения Deepdash для Lodash.
Этот будет аккуратно возвращать false для пустых слотов.