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 create a tree-like nested structure as obj (later JSON) but have struggling to do that properly.
I want to convert this:
root/app/index.html
into this:
{
type: 'box',
name: 'root'
children: {
type: 'box',
name: 'app',
children: {
type: 'item',
name: 'index.html'
}
}
}
How I can do that by code any advice?
You could also use a recursive approach:
let str = 'root/app/index.html';
console.log(convertToNestedTree(str))
function convertToNestedTree(str) {
let members = str.split('/');
if(members.length === 1) {
return ({type:'item', name:str});
}
return ({type:'box', name:members[0],
children:convertToNestedTree(members.slice(1).join('/'))
});
}
You could create a function that takes string and split it into array and then you could use reduce method on that array to build nested structure.
const string = 'root/app/index.html';
const parse = str => {
const result = {}
str.split('/').reduce((r, name, i, a) => {
Object.assign(r, {name, type: a[i + 1] ? 'box' : 'item'})
return a[i + 1] ? (r.children = {}) : r
}, result)
return result;
}
console.log(parse(string))
I have array with some values :
let typeArray = ["name", "strret", "car", "type"];
And I have one object :
let formDefinitionObject = {schema:{}}
I want the formDefinitionObject object be like :
let formDefinitionObject = {
schema: {
name: {
type: 'string',
title: 'Name',
required: true
},
strret: {
type: 'string',
title: 'Strret'
},
car: {
type: 'string',
title: 'Car'
},
type: {
type: 'string',
title: 'Type'
}
}
}
I want dynamically for each item in array to be object in formDefinitionObject.schema object. For example if i add one more item in array typeArray.push('country') to automatic add this object in formDefinitionObject.schema object.
Couldn't understand how required: true would fit in. Remaining things can be done as follows
var getFormDefinition = function(arr) {
function getSchemaObj(arr) {
return arr.map(d => ({
[d]: {
type: typeof(d),
title: d
}
}))
.reduce((a, b) => ({ ...a, ...b }))
}
var schemaObj = getSchemaObj(arr)
arr.push = function(...d) {
Object.assign(schemaObj, getSchemaObj(d))
return Array.prototype.push.call(arr, ...d)
}
return ({
schema: schemaObj
})
}
var typeArray = ["name", "strret", "car", "type"];
var result = getFormDefinition(typeArray)
console.log(result)
typeArray.push('nitish')
console.log(result)
Even though you did not clarify more how a field has to be required or does any field has to be as string, here's a solution based on what you provided so far.
The next snippet does the job and it has some explanations :
let typeArray = ['name', 'strret', 'car', 'type'],
formDefinitionObject = {
schema: {}
};
/** cycle through "typeArray" and populate "formDefinitionObject.schema" **/
typeArray.forEach(el => {
let currObj = {
type: 'string', /** YOU DID NOT SPECIY HOW TO DECIDE THE TYPE **/
title: el[0].toUpperCase() + el.substring(1), /** the title with the first letter being capitalized as you provided in the question. You can just use "el" instead of "el[0].toUpperCase() + el.substring(1)" if you'd like to print as it is **/
};
el === 'name' && (currObj['required'] = true); /** YOU DID NOT SPECIY HOW TO DECIDE IF A FIELD HAS TO BE REQUIRED. I just saw only the "name" as required so I did a basic (yet a stupid) check if the current element is "name" add a required to it **/
formDefinitionObject.schema[el] = currObj; /** add the current record to the "schema" attribute **/
});
console.dir(formDefinitionObject); /** printing the result **/
I'll be here if you answer our questions in the comments section.
Til then, hope I pushed you further.
You could use Proxy on the typeArray with the set trap so each time you push new value to the proxy array you can also add new property to your schema. This way you can simulate observer pattern.
You can also create some pattern to add additional properties like required for example name:prop1:prop2 but this way value is fixed to true.
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => schema[name][param] = true);
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone');
console.log(formDefinitionObject)
Update: You could use something like this name:prop1|value:prop2 to add property value other then true but if you don't specify value default is still true
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => {
const [key, value] = param.split('|');
schema[name][key] = value ? value : true
});
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone:default|123/555-333:required');
proxyArray.push('address')
console.log(formDefinitionObject)
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 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>