I have a modal on server side having object like
location: {
lat: {
type: Number
},
lng: {
type: Number
}
},
Now I want to append the values to lat and lng inside the location object. How can I achieve that? I know we can append the values to location like
formData.append("location", value);
I hope I can help, I did it in a simpler way and I believe it works for all hypotheses, please test.
If it doesn't fit your use case, please comment so I can edit my answer.
Full Code:
const parses = []
const fKey = key => ((function until(value, comp = value) {
const result = value.replace(/\.([A-z0-9]*)(\.|$)/, '[$1]$2')
return comp !== result ? until(result, value) : result
})(key))
function populateFormData(values, form = new FormData(), base = '') {
Object.keys(values).forEach(key => {
const value = values[key]
if (typeof value == 'string' ||
typeof value == 'number' ||
typeof value == 'boolean' ||
value instanceof File ||
value instanceof Blob)
{
form.append(fKey(`${base}${key}`), value)
parses.push({
key: `${fKey(`${base}${key}`)}`,
value
})
}
else if (typeof value == 'object')
{
populateFormData(value, form, `${base}${key}.`)
}
})
return form;
}
populateFormData({
title: 'Lucas',
text: 'Is good :)',
preview: {
p_title: 'I am a P title',
p_text: 'I am a P text',
test: {
example: 2,
my: {
obj: [
'eba',
{
hyper: 'text'
},
123
],
yes: true
}
}
}
})
console.log(parses)
i would like to push keys inside array if found undefined or null
const obj = {
name:'ab',
edu:'av',
degres:{
a1:'',
b1:'1'
},
platform:undefined
}
i want an output like
`['a1','platform']`
as the value for a1 and platform were null and undefined
i have treid this solution but it doesnt work
function iterater(obj){
let blankValues = [];
Object.keys(obj).map((key) => {
if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
iterater(obj[key])
} else {
if (typeof obj[key] === "undefined" || obj[key] === ''){
blankValues.push(key);
}
}
})
return blankValues;
}
but this somehow only return ['platform'] only,but the expected output should be ['platform','a1'],i think when running iterater(obj[key]),the value of array (blankValues) gets blank as it doesnt perserve it,but please help me with appropriate logic and structure
The issue is because you're re-defining blankValues as an empty array in every iteration of the recursive loop. To fix this you could accept the array as an optional argument of the function so that values get pushed to it on each iteration.
Also note that, as #ziggy wiggy pointed out in the comments, your logic will fail when a null value is encountered as typeof obj[key] === "object" would be true. You need a specific null check too.
const obj = {
name: 'ab',
edu: 'av',
degres: {
a1: '',
b1: '1'
},
platform: undefined,
foo: null
}
function iterater(obj, arr) {
arr = arr || [];
Object.keys(obj).map((key) => {
if (obj.hasOwnProperty(key) && (typeof obj[key] === "object") && obj[key] !== null) {
iterater(obj[key], arr)
} else {
if (typeof obj[key] === "undefined" || obj[key] === null || obj[key].trim() === '') {
arr.push(key);
}
}
})
return arr;
}
console.log(iterater(obj));
Note that I also added a trim() call to test the empty string. Your previous logic would accept whitespace-filled strings as valid values.
As you said yourself, when you call iterater(obj[key]) it sets a new local blankValues and puts values in it. So i think you should put blankValues outside the function.
And then you don't have to return it (or you can if you want it as a return value).
Or you can pass blankValues as a parameter of iterater in both the main call and the "inside" call
You need to consume the result of recursive call. For example add it back to blankValues like this blankValues.push(...iterater(obj[key]))
const obj = {
name:'ab',
edu:'av',
degres:{
a1:'',
b1:'1'
},
platform:undefined
}
function iterater(obj){
let blankValues = [];
Object.keys(obj).map((key) => {
if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
blankValues.push(...iterater(obj[key]))
} else {
if (typeof obj[key] === "undefined" || obj[key] === ''){
blankValues.push(key);
}
}
})
return blankValues;
}
console.log(iterater(obj))
You must push the result returned by the recursive call to your array.
Change:
iterater(obj[key])
for:
blankValues.push(...iterater(obj[key]))
const obj = {
name: 'ab',
edu: 'av',
degres: {
a1: '',
b1: '1'
},
platform: undefined
}
function iterater(obj){
let blankValues = [];
Object.keys(obj).map((key) => {
if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) {
blankValues.push(...iterater(obj[key]))
} else {
if (typeof obj[key] === "undefined" || obj[key] === ''){
blankValues.push(key);
}
}
})
return blankValues;
}
console.log(iterater(obj));
Here is another way to do it using Object.entries(), Object.keys(), Array.reduce(), Array.flat() and Array.isArray(). This implementation works for arrays too.
const obj = {
name:'ab',
edu:'av',
something: [{ a: 1 }, { a: '' }],
degres:{
a1:'',
b1:'1'
},
platform:undefined
};
function getEmptyProps(obj) {
if (!Object.keys(obj).length) { return []; }
return Object.entries(obj).reduce((acc, [key, val]) => {
if (val === undefined || val === null || val.toString().trim() === '') {
acc.push(key);
} else if (Array.isArray(val)) {
acc.push(val.map(getEmptyProps).flat());
} else if (typeof val === 'object') {
acc.push(getEmptyProps(val));
}
return acc.flat();
}, []);
}
console.log(getEmptyProps(obj))
You could take a check for falsy keys and return the key, if the property is an object, the check the object.
const
getFalsy = o => Object.keys(o).reduce((r, k) => {
if (!o[k]) return [...r, k];
if (typeof o[k] === 'object') return [...r, ...getFalsy(o[k])];
return r;
}, []),
object = { name: 'ab', edu: 'av', degres: { a1: '', b1: '1' }, platform: undefined };
console.log(getFalsy(object));
I am a JS newbie trying to extract some value from an array of json maps.
The map is something like:
var tags = [{
Key: 'backup',
Value: 'true'
},
{
Key: 'Name',
Value: 'sdlc-root'
}
]
// Here is my first attempt:
var volName = tags.filter(function(item) {
return item.Key === 'Name';
})
.map(result => {
return result.Value;
});
console.log(volName);
The result is: [ 'sdlc-root' ] , but I only need the String value.
The temporary solution I take for now is:
var volName = tags.filter(function(item) { return item.Key === 'Name'; })
.map(result => { return result.Value; })**[0]**;
console.log(volName);
The result is: sdlc-root
I hate my temporary solution, and would like to hear some advice for improvement or alternatives from experienced developers
You could find the element or a default object and take the wanted property.
var volName = (tags.find(({ Key }) => Key === 'Name') || {}).Value;
Write a custom function like below
var tags = [{
Key: 'backup',
Value: 'true'
},
{
Key: 'Name',
Value: 'sdlc-root'
}
]
function f(tags) {
for (i = 0; i <= tags.length; i++) {
if (tags[i] && tags[i]['Key'] === 'Name') {
return tags[i]['Value']
}
}
}
console.log(f(tags))
const tagsObj = tags.reduce((a, c) => { a[c.Key] = c.Value; return a }, {})
// {backup: "true", Name: "sdlc-root"}
console.log(tagsObj["Name"])
// "sdlc-root"
I'm working on a search filter for mine object. But he only shows exact result like:
Array data:
name => 'foo',
name => 'fa',
name => 'bar',
What i've is:
getObjects(array, 'name', 'foo');
If i search on 'f' i get nothing. when i search on 'foo' i get 'foo'. What i want is, if i search on 'f', i want 'foo' and 'fa'.
Code
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] === 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i == key && obj[key] == val) {
objects.push(obj);
}
}
return objects;
}
I like to hear from you, thanks a lot!
var a = 'blabla';
alert(a.includes('la'))
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] === 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i.includes(key) && obj[key].includes(val)) {
objects.push(obj);
}
}
return objects;
}
You could use String#indexOf, or with ES6 String#includes
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] === 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i.indexOf(key) !== -1 && obj[key].indexOf(val) !== -1) {
objects.push(obj);
}
}
return objects;
}
var array = [{ name: 'foo' }, { name: 'fa' }, { name: 'bar' }];
console.log(getObjects(array, 'name', 'f'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let's make this a lot simpler.
If I read the question correctly, this is your data:
var array = [{name: 'foo'},{name: 'fa'},{name: 'bar'}];
Then it's not that much work to get all the items where a specific key contains a specific value:
var array = [{name: 'foo'},{name: 'fa'},{name: 'bar'}];
function getObjects(array, key, value) {
return array.filter(function(item) { // I only want items that...
return item[key] && // Have the current key
item[key].includes(value); // And it contains the value I'm looking for.
});
}
console.log(getObjects(array, 'name', 'foo'));
console.log(getObjects(array, 'name', 'a'));
console.log(getObjects(array, 'name', 'f'));
console.log(getObjects(array, 'baz', 'woo')); // No results for bad key.
Note that you could use item.hasOwnproperty(key) && instead of item[key] &&, if that's something you need to worry about.
for
i == key && obj[key] == val
you can use
i.includes(key) && obj[key].includes(val)
I am trying to return a specific node in a JSON object structure which looks like this
{
"id":"0",
"children":[
{
"id":"1",
"children":[...]
},
{
"id":"2",
"children":[...]
}
]
}
So it's a tree-like child-parent relation. Every node has a unique ID.
I'm trying to find a specific node like this
function findNode(id, currentNode) {
if (id == currentNode.id) {
return currentNode;
} else {
currentNode.children.forEach(function (currentChild) {
findNode(id, currentChild);
});
}
}
I execute the search for example by findNode("10", rootNode). But even though the search finds a match the function always returns undefined. I have a bad feeling that the recursive function doesn't stop after finding the match and continues running an finally returns undefined because in the latter recursive executions it doesn't reach a return point, but I'm not sure how to fix this.
Please help!
When searching recursively, you have to pass the result back by returning it. You're not returning the result of findNode(id, currentChild), though.
function findNode(id, currentNode) {
var i,
currentChild,
result;
if (id == currentNode.id) {
return currentNode;
} else {
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
for (i = 0; i < currentNode.children.length; i += 1) {
currentChild = currentNode.children[i];
// Search in the current child
result = findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}
function findNode(id, currentNode) {
if (id == currentNode.id) {
return currentNode;
} else {
var result;
currentNode.children.forEach(function(node){
if(node.id == id){
result = node;
return;
}
});
return (result ? result : "No Node Found");
}
}
console.log(findNode("10", node));
This method will return the node if it present in the node list. But this will loop through all the child of a node since we can't successfully break the forEach flow. A better implementation would look like below.
function findNode(id, currentNode) {
if (id == currentNode.id) {
return currentNode;
} else {
for(var index in currentNode.children){
var node = currentNode.children[index];
if(node.id == id)
return node;
findNode(id, node);
}
return "No Node Present";
}
}
console.log(findNode("1", node));
I use the following
var searchObject = function (object, matchCallback, currentPath, result, searched) {
currentPath = currentPath || '';
result = result || [];
searched = searched || [];
if (searched.indexOf(object) !== -1 && object === Object(object)) {
return;
}
searched.push(object);
if (matchCallback(object)) {
result.push({path: currentPath, value: object});
}
try {
if (object === Object(object)) {
for (var property in object) {
if (property.indexOf("$") !== 0) {
//if (Object.prototype.hasOwnProperty.call(object, property)) {
searchObject(object[property], matchCallback, currentPath + "." + property, result, searched);
//}
}
}
}
}
catch (e) {
console.log(object);
throw e;
}
return result;
}
Then you can write
searchObject(rootNode, function (value) { return value != null && value != undefined && value.id == '10'; });
Now this works on circular references and you can match on any field or combination of fields you like by changing the matchCallback function.
Since this old question has been brought back up, here's a different approach. We can write a fairly generic searchTree function which we then use in a findId function. searchTree does the work of traversing the object; it accepts a callback as well as the tree; the callback determines if a node matches. As well as the node, the callback is supplied two functions, next and found, which we call with no parameters to signal, respectively, that we should proceed or that we've found our match. If no match is found, we return null.
It looks like this:
const searchTree = (fn) => (obj) =>
Array.isArray(obj)
? obj.length == 0
? null
: searchTree (fn) (obj [0]) || searchTree (fn) (obj .slice (1))
: fn (
obj,
() => searchTree (fn) (obj .children || []),
() => obj
)
const findId = (target, obj) => searchTree (
(node, next, found) => node.id == target ? found () : next(),
) (tree)
const tree = {id: 1, name: 'foo', children: [
{id: 2, name: 'bar', children: []},
{id: 3, name: 'baz', children: [
{id: 17, name: 'qux', children: []},
{id: 42, name: 'corge', children: []},
{id: 99, name: 'grault', children: []}
]}
]}
console .log (findId (42, tree))
console .log (findId (57, tree))
This code is specific to the structure where subnodes are found in an array under the property children. While we can make this more generic as necessary, I find this a common structure to support.
There is a good argument that this would be better written with mutual recursion. If we wanted, we could get the same API with this version:
const searchArray = (fn) => ([x, ...xs]) =>
x === undefined
? null
: searchTree (fn) (x) || searchArray (fn) (xs)
const searchTree = (fn) => (obj) =>
fn (
obj,
() => searchArray (fn) (obj .children || []),
(x) => x
)
This works the same way. But I find the code cleaner. Either should do the job, though.
We use object-scan for our data processing needs. It's conceptually very simple, but allows for a lot of cool stuff. Here is how you could solve your question
// const objectScan = require('object-scan');
const findNode = (id, input) => objectScan(['**'], {
abort: true,
rtn: 'value',
filterFn: ({ value }) => value.id === id
})(input);
const data = { id: '0', children: [{ id: '1', children: [ { id: '3', children: [] }, { id: '4', children: [] } ] }, { id: '2', children: [ { id: '5', children: [] }, { id: '6', children: [] } ] }] };
console.log(findNode('6', data));
// => { id: '6', children: [] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Similar questions were answered several times, but I just want to add a universal method that includes nested arrays
const cars = [{
id: 1,
name: 'toyota',
subs: [{
id: 43,
name: 'supra'
}, {
id: 44,
name: 'prius'
}]
}, {
id: 2,
name: 'Jeep',
subs: [{
id: 30,
name: 'wranger'
}, {
id: 31,
name: 'sahara'
}]
}]
function searchObjectArray(arr, key, value) {
let result = [];
arr.forEach((obj) => {
if (obj[key] === value) {
result.push(obj);
} else if (obj.subs) {
result = result.concat(searchObjectArray(obj.subs, key, value));
}
});
console.log(result)
return result;
}
searchObjectArray(cars, 'id', '31')
searchObjectArray(cars, 'name', 'Jeep')
I hope this helps someone
I really liked a tree search! A tree is an extremely common data structure for most of today's complex structured tasks. So I just had similar task for lunch too. I even did some deep research, but havent actually found anything new! So what I've got for you today, is "How I implemented that in modern JS syntax":
// helper
find_subid = (id, childArray) => {
for( child of childArray ) {
foundChild = find_id( i, child ); // not sub_id, but do a check (root/full search)!
if( foundChild ) // 200
return foundChild;
}
return null; // 404
}
// actual search method
find_id = (id, parent) => (id == parent.id) : parent : find_subid(id, parent.childArray);
Recursive structure search, modification, keys/values adjustments/replacement.
Usage Example:
const results = []; // to store the search results
mapNodesRecursively(obj, ({ v, key, obj, isCircular }) => {
// do something cool with "v" (or key, or obj)
// return nothing (undefined) to keep the original value
// if we search:
if (key === 'name' && v === 'Roman'){
results.push(obj);
}
// more example flow:
if (isCircular) {
delete obj[key]; // optionally - we decide to remove circular links
} else if (v === 'Russia') {
return 'RU';
} else if (key.toLocaleLowerCase() === 'foo') {
return 'BAR';
} else if (key === 'bad_key') {
delete obj[key];
obj['good_key'] = v;
} else {
return v; // or undefined, same effect
}
});
Tips and hints:
You can use it as a search callback, just return nothing (won't affect anything) and pick values you need to your Array/Set/Map.
Notice that callback is being run on every leaf/value/key (not just objects).
Or you can use the callback to adjust particular values and even change keys. Also it automatically detects circular loops and provides a flag for you to decide how to handle them.
The code
(uses ES6)
Function itself + some example demo data
function mapNodesRecursively(obj, mapCallback, { wereSet } = {}) {
if (!wereSet) {
wereSet = new Set();
}
if (obj && (obj === Object(obj) || Array.isArray(obj))) {
wereSet.add(obj);
for (let key in obj) {
if (!obj.hasOwnProperty(key)){
continue;
}
let v = obj[key];
const isCircular = wereSet.has(v);
const mapped = mapCallback({ v, key, obj, isCircular });
if (typeof (mapped) !== 'undefined') {
obj[key] = mapped;
v = mapped;
}
if (!isCircular) {
mapNodesRecursively(v, mapCallback, { wereSet });
}
}
}
return obj;
}
let obj = {
team: [
{
name: 'Roman',
country: 'Russia',
bad_key: 123,
},
{
name: 'Igor',
country: 'Ukraine',
FOO: 'what?',
},
{
someBool: true,
country: 'Russia',
},
123,
[
1,
{
country: 'Russia',
just: 'a nested thing',
a: [{
bad_key: [{
country: 'Russia',
foo: false,
}],
}],
},
],
],
};
// output the initial data
document.getElementById('jsInput').innerHTML = JSON.stringify(obj, null, 2);
// adding some circular link (to fix with our callback)
obj.team[1].loop = obj;
mapNodesRecursively(obj, ({ v, key, obj, isCircular }) => {
if (isCircular) {
delete obj[key]; // optionally - we decide to remove circular links
} else if (v === 'Russia') {
return 'RU';
} else if (key.toLocaleLowerCase() === 'foo') {
return 'BAR';
} else if (key === 'bad_key') {
delete obj[key];
obj['good_key'] = v;
} else {
return v;
}
});
// output the result - processed object
document.getElementById('jsOutput').innerHTML = JSON.stringify(obj, null, 2);
.col {
display: inline-block;
width: 40%;
}
<div>
<h3>Recursive structure modification, keys/values adjustments/replacement</h3>
<ol>
<li>
Replacing "Russia" values with "RU"
</li>
<li>
Setting the value "BAR" for keys "FOO"
</li>
<li>
Changing the key "bad_key" to "good_key"
</li>
</ol>
<div class="col">
<h4>BEFORE</h4>
<pre id="jsInput"></pre>
</div>
<div class="col">
<h4>AFTER</h4>
<pre id="jsOutput"></pre>
</div>
</div>