I have the following object
{ join: {} }
I'd like to find it's default object from the array below
[
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
I'd like to loop through the array and match the key, in this case 'join'.
This is what I have so far:
var butt_to_find = { join: {} }
var all_buttons = 'array above'
var matching = _.find(all_buttons, function(default_button){
return if default_butt key # 1 is the same as butt_to_find key # 1;
});
This is the first time I've used underscore after hearing so much about it.
Any help, more than welcome
var buttons = [
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
_.find(buttons, function (button) { return 'join' in button })
The problem is that you're using a suboptimal data structure. This would make more sense, and produce simpler code:
var buttons = {
login: {label: 'Login', url: '#login'},
join: {label: 'Join', url: '#join', theme: 'a'},
home: {label: 'none', icon: 'home', url: '#', theme: 'a'}
}
buttons.join // equivalent to the `_.find` line in the first example (but much simpler)
Perhaps you're using an array because the order of the buttons is important. In this case, I'd use an array of arrays:
var buttons = [
['login', {label: 'Login', url: '#login'}],
['join', {label: 'Join', url: '#join', theme: 'a'}],
['home', {label: 'none', icon: 'home', url: '#', theme: 'a'}]
]
_.find(buttons, function (button) { return button[0] === 'join' })
var matching =
( _.find
( all_buttons,
function (button)
{ return _.keys(butt_to_find)[0] in button;
}
)
);
where _.keys(butt_to_find) evaluates to ['join'] (an array containing the keys of butt_to_find), _.keys(butt_to_find)[0] evaluates to 'join' (the first element of said array), and _.keys(butt_to_find)[0] in button evaluates to either true or false, depending whether button contains 'join' as a key. (The in operator is a regular JavaScript operator, not something added by underscore.js.)
var def = {join: {}}
var defs = [
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
_.find(defs,function(item,key){
return _.has(item,_.keys(def)[0])
})
You can also switch to the lodash library (a drop in version of underscore) and do this
_.compact(_.pluck(defs,_.keys(def)[0]))
Related
Given this object:
const data = {
home_tabs: 2,
home_tabs_0_tab_alignment: "left",
home_tabs_0_tab_content: "<h1>hello world</h1>",
home_tabs_0_tab_cta: {
title: 'Learn More',
url: 'https://',
target: ''},
home_tabs_0_tab_icon:"fa-brands",
home_tabs_0_tab_image: 473,
home_tabs_0_tab_title:"Facebook",
home_tabs_1_tab_alignment: "right",
home_tabs_1_tab_content: "<h1>new world</h2>",
home_tabs_1_tab_cta: {
title: 'Learn More',
url: 'https://',
target: ''},
home_tabs_1_tab_icon:"fa-brands",
home_tabs_1_tab_image:851,
home_tabs_1_tab_title:"Twitter"
}
How would I be able to remap it to something like this?
const home_tabs_array = [
{
image_alignment: 'left',
content: '<h1>hello world</h1>',
cta: {
title: 'Learn More',
url: 'https://',
target: '',
},
icon: 'fa-brands',
image: 851,
title: 'Facebook',
},
{
image_alignment: 'right',
content: '<h1>new world</h2>',
cta: {
title: 'Learn More',
url: 'https://',
target: '',
},
icon: 'fa-brands',
image: 473,
title: 'Twitter',
},
];
My current attempt is something like this:
const count = props.home_tabs;
let home_tabs_array = [];
// loop through the number of tabs
for (let i = 0; i < count; i++) {
// create an object for each tab
const tab = {
image_alignment: props.home_tabs_[i]_image_alignment,
content: props.home_tabs_[i]_content,
cta: {
title: props.home_tabs_[i]_cta_title,
url: props.home_tabs_[i]_cta_url,
target: props.home_tabs_[i]_cta_target,
},
icon: props.home_tabs_[i]_icon,
image: props.home_tabs_[i]_image,
title: props.home_tabs_[i]_title,
};
// push the object to the array
home_tabs_array.push(tab);
}
But that's a no go b/c of the []. But I don't know how to access the number in the middle of the key.
Maybe I'm even looking at this the wrong way? How to access the number inside the key of the incoming object?
Here is an answer that constructs the tabs array by traversing the data properties:
const data = {
home_tabs: 2,
home_tabs_0_tab_alignment: "left",
home_tabs_0_tab_content: "<h1>hello world</h1>",
home_tabs_0_tab_cta: { title: 'Learn More', url: 'https://', target: ''},
home_tabs_0_tab_icon:"fa-brands",
home_tabs_0_tab_image: 473,
home_tabs_0_tab_title:"Facebook",
home_tabs_1_tab_alignment: "right",
home_tabs_1_tab_content: "<h1>new world</h2>",
home_tabs_1_tab_cta: { title: 'Learn More', url: 'https://', target: ''},
home_tabs_1_tab_icon:"fa-brands",
home_tabs_1_tab_image:851,
home_tabs_1_tab_title:"Twitter"
}
let tabs = [];
Object.keys(data).forEach(key => {
let m = key.match(/^home_tabs_([0-9]+)_tab_(.*)$/);
if(m) {
let idx = m[1]; // tab index
let prop = m[2]; // property name
if(prop === 'alignment') {
prop = 'image_alignment'; // exception
}
if(!tabs[idx]) {
tabs[idx] = {}; // create empty object at index if not exist
}
tabs[idx][prop] = data[key]; // set the property at the index
}
});
console.log(tabs);
We can use Template literals to get the dynamic value
Note: pay attention to the property inside cta,we need to get value like below
props[`home_tabs_${i}_tab_cta`][`title`]
Test code:
const props = {
home_tabs: 2,
home_tabs_0_tab_alignment: "left",
home_tabs_0_tab_content: "<h1>hello world</h1>",
home_tabs_0_tab_cta: {
title: 'Learn More',
url: 'https://',
target: ''},
home_tabs_0_tab_icon:"fa-brands",
home_tabs_0_tab_image: 473,
home_tabs_0_tab_title:"Facebook",
home_tabs_1_tab_alignment: "right",
home_tabs_1_tab_content: "<h1>new world</h2>",
home_tabs_1_tab_cta: {
title: 'Learn More',
url: 'https://',
target: ''},
home_tabs_1_tab_icon:"fa-brands",
home_tabs_1_tab_image:851,
home_tabs_1_tab_title:"Twitter"
}
const count = props.home_tabs;
let home_tabs_array = [];
// loop through the number of tabs
for (let i = 0; i < count; i++) {
// create an object for each tab
const tab = {
image_alignment: props[`home_tabs_${i}_tab_alignment`],
content: props[`home_tabs_${i}_tab_content`],
cta: {
title: props[`home_tabs_${i}_tab_cta`][`title`],
url: props[`home_tabs_${i}_tab_cta`][`url`],
target: props[`home_tabs_${i}_tab_cta`][`target`],
},
icon: props[`home_tabs_${i}_tab_icon`],
image: props[`home_tabs_${i}_tab_image`],
title: props[`home_tabs_${i}_tab_title`],
};
// push the object to the array
home_tabs_array.push(tab);
}
console.log(home_tabs_array)
You should use string interpolation during object property selection, like this:
const count = props.home_tabs;
let home_tabs_array = [];
// loop through the number of tabs
for (let i = 0; i < count; i++) {
// create an object for each tab
const tab = {
image_alignment: props[`home_tabs_[${i}]_image_alignment`],
content: props[`home_tabs_[${i}]_content`],
cta: {
title: props[`home_tabs_[${i}]_cta_title`],
url: props[`home_tabs_[${i}]_cta_url`],
target: props[`home_tabs_[${i}]_cta_target`],
},
icon: props[`home_tabs_[${i}]_icon`],
image: props[`home_tabs_[${i}]_image`],
title: props[`home_tabs_[${i}]_title`],
};
// push the object to the array
home_tabs_array.push(tab);
}
Apologies if title is not clear.
I am using json2csv npm package to prepare csv from json object and this package allows us to add a hook to transform object before actual csv line is prepared.
I only need to manipulate two properties out of all. How can I do this effectively? My code feels too bloated.
const {
Parser: Json2csvParser,
transforms: { unwind },
} = require('json2csv');
const json2csvFields = [
{ value: 'root.filename', label: 'File Name' },
{ value: 'issue.root.priority', label: 'Priority' },
{ value: 'issue.root.url', label: 'URL' },
{ value: 'issue.root.startline', label: 'Start Line' },
{ value: 'issue.root.stopline', label: 'Stop Line' },
{ value: 'issue.root.startcolumn', label: 'Start Column' },
{ value: 'issue.root.stopcolumn', label: 'Stop Column' },
{ value: 'issue.root.issuename', label: 'Issue Name' },
{ value: 'issue.root.issuecategory', label: 'Issue Category' },
{ value: 'issue._', label: 'Issue Description' },
];
const sampleData = [
{
root: {
filename:
'/home/users/john-doe/workspace/foo-project/src/main/classes/foo.cls',
},
issue: {
root: {
priority: 1,
url: 'www.example.com',
startline: 100,
stopline: 105,
startcolumn: 20,
stopcolumn: 25,
issuename: 'blah',
issuecategory: 'Category A',
},
_: ' Fox ',
},
},
];
const json2csvOptions = {
fields: json2csvFields,
quote: '',
header: true,
transforms: [
(item) => ({
'root.filename': item.root.filename.replace(
'/home/users/john-doe/workspace/foo-project/src/main/classes/',
''
),
'issue._': `"${item.issue._.trim()}"`,
// Except for the above two, everything else doens't need any transformation.
'issue.root.priority': item.issue.root.priority,
'issue.root.url': item.issue.root.url,
'issue.root.startline': item.issue.root.startline,
'issue.root.stopline': item.issue.root.stopline,
'issue.root.startcolumn': item.issue.root.startcolumn,
'issue.root.stopcolumn': item.issue.root.stopcolumn,
'issue.root.issuename': item.issue.root.issuename,
'issue.root.issuecategory': item.issue.root.issuecategory,
}),
],
};
const json2csvParser = new Json2csvParser(json2csvOptions);
const csv = json2csvParser.parse(sampleData);
console.log(csv);
This prints below output:
File Name,Priority,URL,Start Line,Stop Line,Start Column,Stop Column,Issue Name,Issue Category,Issue Description
foo.cls,1,www.example.com,100,105,20,25,blah,Category A,"Fox"
EDIT: Updated code to a working example.
After listing the two properties with special treatment, use Object.fromEntries and Object.entries to transform all the issue.root properties to their flat structure with .s in the property names. Then that object can be spread into the returned object.
const transformsFn = ({ root, issue }) => ({
'root.filename': root.filename.replace(
'/home/users/john-doe/workspace/foo-project/src/main/classes/',
''
),
'issue._': `"${issue._.trim()}"`,
...Object.fromEntries(
Object.entries(issue.root).map(
([key, val]) => [`issue.root.${key}`, val]
)
),
});
const json2csvOptions = {
fields: json2csvFields,
quote: '',
header: true,
transforms: [transformsFn],
};
I try to create sidebar component in React and I have a data structure like bellow
const links = [
{
label: 'Item-1',
url: '/item-1',
},
{
label: 'Item-2',
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
},
{
label: 'Item-2-2',
url: '/item-2-2',
},
{
label: 'Item-2-3',
url: '/item-2-3',
},
],
},
{
label: 'Item-3',
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
},
{
label: 'Item-3-2',
url: '/item-3-2',
},
],
},
];
So the problem is let's say the user changed URL and URL something like that http://localhost:90/item-2-3.
And I need to activate this sidebar item from the sidebar and it can be nested. So firstly I need to deactivate all other sidebar items (I don't want multiple selected items in the sidebar) and after that activate the selected sidebar item.
Because that (if I'm correct) I need update all tree items let's say I add active:false field to all JSON and after that I need to find the correct tree item from tree (URL === item-2-3) add active:true and also update all parent json to active:true (for there are look selected/opened)
So my question is am I correct if I correct how can write this code optimal way? :/
Actually, I want to create a function and when calling this function like that selectItemFromTree(links, '/item-2-2') I get results like in bellow.
const links = [
{
label: 'Item-1',
url: '/item-1',
active: false
},
{
label: 'Item-2',
active: true,
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
active: false
},
{
label: 'Item-2-2',
url: '/item-2-2',
active: true,
},
{
label: 'Item-2-3',
url: '/item-2-3',
active: false
},
],
},
{
label: 'Item-3',
active: false,
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
active: false
},
{
label: 'Item-3-2',
url: '/item-3-2',
active: false
},
],
},
];
You have to traverse the tree recursively and update all the active statuses.
const links=[{label:"Item-1",url:"/item-1"},{label:"Item-2",children:[{label:"Item-2-1",url:"/item-2-1"},{label:"Item-2-2",url:"/item-2-2"},{label:"Item-2-3",url:"/item-2-3"}]},{label:"Item-3",children:[{label:"Item-3-1",url:"/item-3-1"},{label:"Item-3-2",url:"/item-3-2"}]}];
const updateActiveLink = (links, url, parent = null) => {
for (link of links) {
link.active = false;
if (link?.children) {
updateActiveLink(link.children, url, link)
}
if (link.url === url) {
link.active = true;
if (!!parent) parent.active = true;
}
}
}
updateActiveLink(links, '/item-2-3');
console.log(links);
.as-console-wrapper {min-height: 100%!important; top: 0}
Note that this method will mutate the links array.
I am trying to use the Inspector's when parameter in a list item. In this particular case, I want to hide the body text field of an item when the user uses a toggle button:
inputs: {
mylist: {
type: 'list',
label: 'List of items',
item: {
type: 'object',
properties: {
attrs: {
text: {
title: {
type: 'text',
label: 'Title',
index: 1
},
body: {
type: 'textarea',
label: 'Body',
index: 2,
when: { eq: {'????': false} } // what path should be used here?
}
},
toggles: {
toggleBody: {
defaultValue: false,
type: 'text',
label: 'Hide body',
index: 3
},
}
} // attrs
} // properties
} // item
}
}
I have verified through renderFieldContent:
renderFieldContent: function(options, path, value) {
if(path.endsWith('/attrs/text/body')){
console.log(path);
}
} // 1 item in the list outputs: mylist/0/attrs/toggles/toggleBody
That the list items paths follow the pattern `mylist/${i}/attrs/toggles/toggleBody` where ${i} is the item number...
I tried to reference mylist/0/attrs/toggles/toggleBody in the expression (so it would always reference the first item), but this also does not seem to work... Is there a way to reference a list item property's path?
From Rappid v3.2, one can place wildcards within the path.
'mylist/${index}/attrs/toggles/toggleBody'
A wildcard ${index} will be dynamically substituted for the actual index of the item inside which the when clause is being evaluated.
inputs: {
mylist: {
type: 'list',
label: 'List of items',
item: {
type: 'object',
properties: {
attrs: {
text: {
title: {
type: 'text',
label: 'Title',
index: 1
},
body: {
type: 'textarea',
label: 'Body',
index: 2,
when: {
eq: {
'mylist/${index}/attrs/toggles/toggleBody': false
},
// otherwise: { unset: true }
// Uncomment above to clear the body text
// from the model when the body is hidden
}
}
},
toggles: {
toggleBody: {
defaultValue: false,
// Changed to built-in toggle
type: 'toggle',
label: 'Hide body',
index: 3
}
}
}
}
}
}
}
I have these two array of objects
todos: [
{
id: 1,
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
name: 'handover',
label: 'Handover (in CRM)'
},
]
And:
todosMoreDetails: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}
]
So that the final array of objects will be a combination of the two, based on the object ID, like below:
FinalTodos: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'handover',
label: 'Handover (in CRM)'
}
]
I tried with merge mergeAll and mergeWithKey but I am probably missing something
You can achieve this with an intermediate groupBy:
Transform the todosMoreDetails array into an object keyed by todo property ID using groupBy:
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
moreDetailsById is an object where the key is id, and the value is an array of todos. If the id is unique, this will be a singleton array:
{
1: [{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}]
}
Now transform the todos array by merging each todo to it's details you retrieve from the grouped view:
var finalTodos = R.map(todo => R.merge(todo, moreDetailsById[todo.id][0]), todos);
An alternate more detailed way:
function mergeTodo(todo) {
var details = moreDetailsById[todo.id][0]; // this is not null safe
var finalTodo = R.merge(todo, details);
return finalTodo;
}
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
var finalTodos = todos.map(mergeTodo);
I guess merge is only used for arrays. Have a search for object "extend". Maybe storing the todo details not in seperate objects is the better solution.
Using jQuery? https://api.jquery.com/jquery.extend/
Using underscore? http://underscorejs.org/#extend
Native approach? https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
Using underscore:
var result = [];
var entry = {};
_.each(todos, function(todo) {
_.each(todosMoreDetails, function(detail) {
if (todo.id == detail.id) {
entry = _.extend(todo, detail);
result.push(entry);
}
}
});
return result;