So, basically, I have this object (in vue data()):
...
detail: {
title: 'I have no idea',
date: 2020-02-12,
sender: {
name: 'John Cenna',
username: 'john10'
},
receipent: {
name: 'Katleen',
username: 'katmeow'
},
....
}
Now, I want to assign all of them to be null (for validation process, kind of) but couldn't assign the nested object (the object without child is fine with just loop). How do I do this? thank you
A recursive solution:
Use Object.entries() to iterate the key-value entries of the object as follows. Note that Object.entries() returns an empty array for non-objects.
If the entry's value is already null or undefined, return.
If the entry's value is an array, recurse on each array item (go to step 1 with the array item as the new object to evaluate).
If the entry's value is an object, recurse on the object.
Otherwise, set the entry's value to null.
const data = {
detail: {
title: 'I have no idea',
date: 2020 - 02 - 12,
sender: {
name: 'John Cenna',
username: 'john10'
},
receipent: {
name: 'Katleen',
username: 'katmeow'
},
items: [
{ id: 100, name: 'Apple' },
{ id: 200, name: 'Banana' },
{ id: 300, name: 'Orange' },
]
}
}
function nullify(obj) {
// 1
Object.entries(obj).forEach(([k,v]) => {
// 2
if (v === null || v === undefined) {
return
}
// 3
if (Array.isArray(v)) {
return v.forEach(nullify)
}
// 4
if (typeof v === 'object') {
nullify(obj[k])
} else {
// 5
obj[k] = null
}
})
}
nullify(data.detail)
console.log('res', data.detail)
Related
I already found out, how to get the last elements of a deep nested object.
See here for working example: How to get the last children in a deeply nested array with objects
Now I dont want to log the names like in the example with console.log(subObj.name), instead I want to save them in an array, which shouldnt be global. I just want a function return this array.
Is this somehow possible without declaring a global array for this ?
This is my code:
function childrenNames (obj) {
var lastChildren = [];
obj.forEach((subObj) => {
if (subObj.hasOwnProperty('specification') && subObj.specification instanceof Array && subObj.specification.length > 0) {
childrenNames(subObj.specification);
} else {
if (subObj.hasOwnProperty('name')) {
lastChildren.push(subObj.name)
}
}
})
console.log(lastChildren);
return lastChildren
}
But its just returning 4 different arrays instead of 1 containing all last children.
I am not sure whether this is a valid answer and I do not understand it but I am leaving it for now because it does appear, superficially at least, to answer the question. It does not require a global array to be declared as far as I can tell?
var obj = [
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
},
{
name: 'something empty',
children: [ ]
}
]
}
]
},
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
}
]
}
]
},
{
name: "children isn't an array",
children: 42
}
]
function childrenNames (obj) {
var lastChildren = [];
obj.forEach((subObj) => {
if (subObj.hasOwnProperty('specification') && subObj.specification instanceof Array && subObj.specification.length > 0) {
childrenNames(subObj.specification);
} else {
if (subObj.hasOwnProperty('name')) {
lastChildren.push(subObj.name)
}
}
})
// console.log(lastChildren);
return lastChildren
}
const res = childrenNames(obj);
console.log('res', res);
What I am trying to achieve is:
Find if the text object within array is empty.
If criteria from no1 is matched, then return id value that sits in the top level of that object.
https://codesandbox.io/s/cranky-swirles-gb6ct?file=/src/App.js:410-412
In the code sandbox's example I have added two objects, with empty text strings and in that case I would expect to get an array of strings back (result = ['662e4120', '782h7a9x'])
I am able to find empty values, however I am not sure how to return object from the upper scope.
If you can't access the codeSandbox, snippet is attached just below:
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}];
const findItem = () => {
return array
.map((item) => item.state)
.map((item) => item.properties)
.flat()
.filter((item) => item.text === "");
};
Try to do something like this https://codesandbox.io/s/jovial-mcnulty-2fwh4
export default function App() {
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}
];
const findItem = () => {
return array.filter(obj=>obj.state.properties[0].text==="").map(obj=>obj.id)
};
console.log(findItem());
return <div className="App"></div>;
}
Here, we are filtering on the original array based on a predicate which is obj=>obj.state.properties[0].text==="". This basically get all the elements of the array which satisfy this predicate function. After this we are just applying map over the result to get the ids of the array elements satisfying this predicate function.
To get an array with the ids of the objects with no text you have to change the order or your iterations.
.filter() the array for the elements with empty text fields.
.map() the remaining elements to the values you are aiming for
When mapping or filtering you can't just go one but as many levels deep as you like. Since 'properties' holds an array and you want the first element, you can access that with the index array[0] (with that the flat() you did is superfluous)
const findItem = () => {
return array
.filter(item => item.state.properties[0].text === "") // (2) [{…}, {…}] the original items with no text
.map(item => item.id) // ['662e4120', '782h7a9x']
};
(code might be as well embedded as a snippet, which can be run directly)
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}];
const findItem = () => {
return array
.filter(item => item.state.properties[0].text === "") // (2) [{…}, {…}] the original items with no text
.map(item => item.id) // ['662e4120', '782h7a9x']
};
console.log(findItem())
I have object:
var roles = { roles: [0: { name: 'admin' }, 1: { name: 'user' }] }
How I can check if value user exists?
I tried do:
console.log(('user' in roles));
But this return false. Why?
With a proper object, you could treat roles.roles as array and find the value with Array#some.
This works for any array like structure with an assignment to an array with Object.assign.
function check(name) {
return Object.assign([], roles.roles).some(o => o.name === name);
}
var roles = { roles: { 0: { name: 'admin' }, 1: { name: 'user' } } };
console.log(check('user'));
console.log(check('bar'));
By taking an array directly, you coult omit the assignment part.
function check(name) {
return roles.roles.some(o => o.name === name);
}
var roles = { roles: [{ name: 'admin' }, { name: 'user' }] };
console.log(check('user'));
console.log(check('bar'));
in operator checks for property not for it's values
let test = {'a':1,'b':2}
console.log('a' in test)
console.log(1 in test)
How can i search values
Here using some method of array i am checking whether desired value is in object or not.
var roles = { roles: [{ name: 'admin' },{ name: 'user' }] }
let searchValue = (input,searchKey) => {
return input.some(( {name} ) => name === searchKey) //
}
console.log(searchValue(roles.roles, 'user'))
console.log(searchValue(roles.roles, 'user not foound'))
Sorry for the title, It's limited to 150 characters.
Full code example:
https://jsfiddle.net/c81zw30m/
Data:
Let's say I make an API request and I get this JSON object returned:
[
{
id: 123,
person: {
data: {
name: 'John',
language: 'Javascript'
}
},
details: {
age: 25
},
has_experience: true
},
{
id: 456,
person: {
data: {
name: 'Peter',
language: null // here we have null as a value.
}
},
details: {
age: 40
},
has_experience: false
},
{
id: 789,
person: {
data: {
name: 'Paul',
language: 'Python'
}
},
details: {
age: 30
},
has_experience: null // and here we also don't know if the person is available
},
];
Goal:
The end goal here is to iterate over the array and end up with new array of objects with different key names. Say for example I want to replace the key of person with human or the key of available with availability.
Additionally (optionally) we want to skip adding keys which value is equal to null.
Current solution:
let results = [];
for (let i=0; i< json.length; i++) {
results.push({
user_id: json[i].id,
name: json[i].person.data.name,
age: json[i].details.age,
has_experience: json[i].available ? json[i].available : false // here we are assigning a value no matter what using a ternary operator, what if we want no key:value pair here, just skip that pair
});
if (json[i].person.data.language) { results[i].language = json[i].person.data.language }
}
console.log(results);
Problem:
Now the example and solution I provided works, but imagine if the original API request had hundreds of key:value pairs, and many of them might be of null value.
Question:
Using modern javascript, is there any less verbose and more clean looking/elegant way to handle this problem?
Overall I am looking to create a brand new array of objects based on the original one, but with new key names where necessary. Additionally, we want to skip adding some of them if the value of the key is null for example.
Cheers.
EDIT:
Changed the key name from the example originally provided from available to has_experience because it was a bit misleading. I am not looking to filter out the original array of objects based on the value of a given key. If I wanted to do that I'd start with filter and then chain on.
What I want to do is to omit adding a key:value pair in the newly formed array if the value of the key is null for example.
Using lodash (or similar), you could get your mapping definition out of the mapping loop.
I find the following reasonably concise, though it can probably be shortened a little further.
import { get, set } from "lodash";
let json = [ ... ];
let mapping = new Map([
["user_id", "id"],
["name", "person.data.name"],
["age", "details.age"],
["availability", "available"],
["language", "person.data.language"],
["some.nested.property", "person.data.language"]
]);
var results = json.map(element => {
var mappedElement = {};
mapping.forEach((path, field, map) => {
var value = get(element, path);
if (value) {
set(mappedElement, field, value);
}
});
return mappedElement;
});
console.log(results);
Running this on your data yields
[Object, Object, Object]
0: Object
user_id: 123
name: "John"
age: 25
availability: true
language: "Javascript"
some: Object
nested: Object
property: "Javascript"
1: Object
user_id: 456
name: "Peter"
age: 40
2: Object
user_id: 789
name: "Paul"
age: 30
language: "Python"
some: Object
nested: Object
property: "Python"
Working example: https://codesandbox.io/s/mnkp79668
You can try something like this
You can achieve with map()
let json = [{id: 123, person: { data: { name: 'John', language: 'Javascript' } }, details: { age: 25 }, has_experience: true },
{id: 456, person: { data: { name: 'Peter',language: null } }, details: { age: 40 }, has_experience: false},
{id: 789, person: { data: { name: 'Paul', language: 'Python' } }, details: { age: 30 }, has_experience: null },];
let results = [];
results = json.map(current => {
let temp = {
user_id: current.id,
name: current.person.data.name,
age: current.details.age,
}
if (current.has_experience) {
temp.availablity = current.has_experience
}
if (current.person.data.language)
{ temp.language = current.person.data.language }
return temp;
})
console.log(results);
You have two separate problems to resolve. The first appears to be a requirement for generic flattening of the nested data structures within the input with out specifying every possible key that might exist.
This function will recursively flatten a nested object, along the way omitting any null values. However, this function might overwrite any values where the same key exists at multiple levels, so see below.
function flatten(obj, dest) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
flatten(obj[key], dest);
} else if (obj[key] !== null) {
dest[key] = obj[key];
}
}
return dest;
}
You also want to re-map some of the keys in your data, where the below function can be used both as a pre-processor to convert known duplicate keys into unique keys, and can also be used as a post-processor to convert particular keys back into nested objects. NB: requires "lodash".
function remap(obj, keys) {
for (let [in_key, out_key] of keys) {
let val = _.get(obj, in_key, null);
if (val !== null) {
_.unset(obj, in_key);
_.set(obj, out_key, val);
}
}
return obj;
}
The functions can be chained together like this:
let in_map = new Map([
['user.id', 'user_id']
]);
let out_map = new Map([
['available', 'test.availability']
]);
let out = data.map(obj => remap(obj, in_map))
.map(obj => flatten(obj, {}))
.map(obj => remap(obj, out_map));
I think what you want is to first filter the list, then map over the filtered results to create the new structure. This may not be especially performant however if the list is quite large.
const list = [
{
id: 123,
person: {
data: {
name: 'John',
language: 'Javascript'
}
},
details: {
age: 25
},
available: true
},
{
id: 456,
person: {
data: {
name: 'Peter',
language: null // here we have null as a value.
}
},
details: {
age: 40
},
available: false
},
{
id: 789,
person: {
data: {
name: 'Paul',
language: 'Python'
}
},
details: {
age: 30
},
available: null // and here we also don't know if the person is available
},
];
const newList = list.filter(listItem => listItem.available).map(filteredItem => {
return {
user_id: filteredItem.id,
name: filteredItem.person.data.name,
age: filteredItem.details.age,
availability: !!filteredItem.available
}
})
document.getElementById('list').innerText = JSON.stringify(list, null, 2);
document.getElementById('newList').innerText = JSON.stringify(newList, null, 2);
.container {
display: flex;
}
.container pre {
flex: 0 0 50%;
}
<div class="container">
<pre id="list"></pre>
<pre id="newList"></pre>
</div>
I read lots of information on the net and forums but cannot get a solution to my problem.
I have the following JSON file:
var data =
{ "AA" :[{"a1":[{"ab1": [
{"ab1a": 10 },
{"ab1b": 20 },
{"ab1c": 30 },
{"ab1d": 40 }
]
},
{"ab2":[
{"ab1a": 10 },
{"ab1b": 20 },
{"ab1c": 30 },
{"ab1d": 40 }
]
}
]
}
,{"a2":[{"ab3": [
{"ab3a": 10 },
{"ab3b": 20 },
{"ab3c": 30 },
{"ab3d": 40 }
]
},
{"ab4":[
{"ab4a": 10 },
{"ab4b": 20 },
{"ab4c": 30 },
{"ab4d": 40 }
]
}
]
}
]
}
I have validated the JSON file. I want to get the keys first for "AA" then "a1" and "a2" and then for "ab1", "ab2" and etc. There are questions and information about values but not the keys. Most probably jQuery might help but I am confused about how to get all the keys.
Any ideas? any hope...!
The key to getting all the keys from an object of unknown size is recursion. Here I have 2 solutions; one Functional and one Object Oriented. Also I created some other data to test with in addition to yours.
Here is the repo with the complete code: https://github.com/vasilionjea/json-keys
The Data:
var data = {
continents: {
europe: {
countries: [
{
'country-name': 'italy',
cities: [
{ 'city-name': 'Rome', title: 'Roma Dolor', population:873, gdb: 301 },
{ 'city-name': 'Venice', title: 'Venice Veggies.', population:456, gdb: 244 }
]
},
{
'country-name': 'france',
cities: [
{ 'city-name': 'Paris', title: 'De Ipsum', population:7123, gdb: 77 },
{ 'city-name': 'Marseille', title: 'La Mipsum', population:73, gdb: 7 }
]
}
]
},
'north-america': {
countries: [
{
'country-name': 'canada',
cities: [
{ 'city-name': 'Montreal', title: 'The city of lorem ipsum!', population:99, gdb: 011 },
{ 'city-name': 'Quebec', title: 'Veggie Ipsum.', population:123, gdb: 101 }
]
},
{
'country-name': 'usa',
cities: [
{ 'city-name': 'New York', title: 'Empire State', population:1001001, gdb: 1010 },
{ 'city-name': 'San Francisco', title: 'SF Bridge', population:20123, gdb: 202 }
]
}
]
}
}
}
The functional way:
/*
* Functional example.
* Retrieves all keys from an object of unknown size using recursion.
*/
function _isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function _eachKey(obj, callback) {
Object.keys(obj).forEach(callback);
}
var _container = [];
function collectKeys(data) {
_eachKey(data, function(key) {
_container.push(key);
if (_isObject(data[key])) {
collectKeys(data[key]);
}
if (Array.isArray(data[key])) {
// Because we're looping through an array and each array's item is an object,
// the `collectKeys` function is receiving each object of the array as an argument.
data[key].forEach(collectKeys);
}
});
return _container;
}
// Execute the function
var myKeys = collectKeys(data);
The Object Oriented way:
/*
* Object Oriented example.
* Retrieves all keys from an object of unknown size using recursion.
*/
function JSONKeys(obj) {
this._container = [];
if (this._isObject(obj)) {
// Just a normal object literal.
this._data = obj;
} else if (typeof obj === 'string') {
// Just in case the data was passed in as a valid JSON string.
this._data = JSON.parse(obj);
} else {
throw new Error('The provided argument must be an object literal or a JSON string.');
}
}
JSONKeys.prototype = {
constructor: JSONKeys,
_isObject: function(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
},
_eachKey: function(obj, callback) {
// Using `bind(this)` makes sure that the `this` value in the `_collect` method
// isn't set to the global object (window).
Object.keys(obj).forEach(callback.bind(this));
},
_recur: function(key, data) {
// If this key's value is also an object go deeper.
if (this._isObject(data[key])) {
this._collect(data[key]);
}
if (Array.isArray(data[key])) {
// * Because we're looping through an array and each array's item is an object,
// the `_collect` method is receiving each object of the array as an argument.
// * Using `bind(this)` makes sure that the `this` value in the `_collect` method
// isn't set to the global object (window).
data[key].forEach(this._collect.bind(this));
}
},
_collect: function(data) {
this._eachKey(data, function(key) {
this._container.push(key);
// Continue collecting
this._recur(key, data);
});
},
getAll: function() {
this._collect(this._data);
return this._container;
}
};
// Create a new instance passing the data.
var keysObject = new JSONKeys(data);
allKeys = keysObject.getAll();