Node.js: How To Catch A Value in JSON - javascript

I am running a Node.js project. This is my response which I get from an API:
assets: [
{
id: '93b1d990-4110-11eb-b553-0b6473e34c34',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.7416_cliperago_test_image_01.jpg',
type: 'institute_logo',
},
{
id: '93bf3390-4110-11eb-bee1-bf5b057e2905',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.8143_cliperago_video_demo_02.mp4',
type: 'pre_roll_video',
},
{
id: '93d3b550-4110-11eb-979a-4ddd3ec99c0f',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.9016_cliperago_video_demo_03.mp4',
type: 'out_roll_video',
}
]
I want to get the name of the object with type out_roll_video
This is my code
axios.get('https://testrider/api/v1/student/bb533310/group_assets')
.then(response => {
let a = response.data.data.clips_assets
console.log(a);
// console.log(response.data.data.clips_assets.assets);
a.forEach(obj => {
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key}`);
if(`${value}` == 'out_roll_video'){
console.log(` ${key}`);
if(`${key}` == 'name'){
console.log(` ${value}`);
}
}
});
console.log('-------------------');
})
.catch(error => {
console.log("failed");
});

You can use .find to get a specific name according to a condition. If you want to get more than one, you can use .filter to get the objects matching, and then get the names using .map:
const assets= [
{
id: '93b1d990-4110-11eb-b553-0b6473e34c34',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.7416_cliperago_test_image_01.jpg',
type: 'institute_logo',
},
{
id: '93bf3390-4110-11eb-bee1-bf5b057e2905',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.8143_cliperago_video_demo_02.mp4',
type: 'pre_roll_video',
},
{
id: '93d3b550-4110-11eb-979a-4ddd3ec99c0f',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.9016_cliperago_video_demo_03.mp4',
type: 'out_roll_video',
}
];
const { name } = assets.find(e => e.type === 'out_roll_video') || {};
console.log(name);
const names = assets
.filter(e => e.type === 'out_roll_video' || e.type === 'pre_roll_video')
.map(e => e.name);
console.log(names);

Related

How to check if a value in an array is present in other object and accordingly return a new object

I have an array
const dataCheck = ["Rohit","Ravi"];
I have another array of object
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
I want to check if any value in dataCheck is present in the userData and then return a new array with the below data
const newData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit", status: "present" },
{ name: "Ravi", status: "present" },
];
I tried to do something using loops but not getting the expected results
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" }
];
let newDataValue = {};
let newData = [];
userData.forEach((user) => {
const name = user.name;
dataCheck.forEach((userName) => {
if (name === userName) {
newDataValue = {
name: name,
status: "present"
};
} else {
newDataValue = {
name: name
};
}
newData.push(newDataValue);
});
});
console.log(newData);
My trial gives me repeated results multiple results which is just duplicates
You should use map() and a Set.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const set = new Set(dataCheck);
const output = userData.map(data => set.has(data.name) ? ({...data, status: "present"}): data)
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
A Set allows for lookups in O(1) time and therefore this algorithm works in O(n) time. If you would use the array for lookups (e.g. using indcludes(), find() etc.) the runtime would be O(n²). Although this will certainly not matter at all for such small arrays, it will become more relevant the larger the array gets.
map() is used here because you want a 1:1 mapping of inputs to outputs. The only thing to determine then is, what the output should be. It is either the input, if the value is not in the Set, or it is the input extended by one property status set to "present". You can check for the presence in a Set using the has() method and can use the ternary operator ? to make the decision which case it is.
const dataCheck = ["Rohit", "Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
// map through every object and check if name property
// exists in data check with help of filter.
// if it exists the length of filter should be 1 so
// you should return { name: el.name, status: "present" } else
// return { name: el.name }
let newData = userData.map((el) => {
if (dataCheck.filter((name) => name === el.name).length > 0) {
return { name: el.name, status: "present" };
} else {
return { name: el.name };
}
});
console.log("newdata: ", newData);
A better approach would be to use map over userData array, find for matching element in dataCheck, if found return matching element + a status key or just return the found element as it is.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const getUpdatedObject = () => {
return userData.map(userData => {
const userDetail = dataCheck.find(data => userData.name === data);
if(userDetail) return {userDetail, status:"present"}
else return {...userData}
});
}
console.log(getUpdatedObject())
Working fiddle
Loop through userData, check if name is includes in dataCheck. If true add status 'present'.
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
for (let user of userData) {
if(dataCheck.includes(user.name)) {
user.status = 'present'
}
}
console.log(userData)
You are seeing repeated results due to the second loop dataCheck.forEach((userName) => { as every loop of dataCheck will fire the if/else statement and add something to the final array. However many values you add to dataCheck will be however many duplicates you get.
Only need to loop through one array and check if the value is in the other array so no duplicates get added.
const dataCheck = ["Rohit", "Ravi"];
const userData = [{ name: "Sagar" }, { name: "Vishal" }, { name: "Rohit" }, { name: "Ravi" }];
let newDataValue = {};
let newData = [];
// loop thru the users
userData.forEach((user) => {
// set the user
const name = user.name;
// check if in array
if (dataCheck.indexOf(name) >= 0) {
newDataValue = {
name: name,
status: "present",
};
}
// not in array
else {
newDataValue = {
name: name,
};
}
newData.push(newDataValue);
});
console.log(newData);
So you will do like this :
const dataCheck = ["Rohit","Ravi"];
const userData = [
{ name: "Sagar" },
{ name: "Vishal" },
{ name: "Rohit" },
{ name: "Ravi" },
];
const newUserData = userData.map( user => {
dataCheck.forEach( data => {
if( data === user.name )
user.status = "present";
});
return user;
} );
console.log( newUserData );

Filtered object search using allowed keys and also a search value

as seen in my title I am facing a problem that needs a solution. I am trying to filter my object, but not only based on keys. But also based on the value of the allowed keys.
This is what I got so far:
const handleSearch = (searchValue) => {
if(searchValue !== '') {
const allowed = ['name', 'title'];
let list = props.search.list;
const filtered = Object.keys(list)
.filter((key) => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = list[key];
return obj;
}, {});
const filteredArray = Object.entries(filtered)
props.search.onChange(filteredArray)
} else {
props.search.onChange(props.search.list)
}
}
Structure of the list object:
0: {name: "John', title: 'Owner'}
1: {name: "Jane", title: 'Admin'}
Wanted results:
Filtered array that shows the keys filtered aswell as the search value.
And I don't know where I should integrate the filtering by the values aswell. This has been giving me a headache for the past few hours. And hope someone here is experienced with these kinds of issues/logic.
Thanks for reading.
Kind regards.
const handleSearch = (searchValue) => {
if (searchValue !== '') {
const allowed = ['name', 'title'];
const list = props.search.list;
const filtered = list
.filter(obj =>
Object.keys(obj)
.some(k => allowed.includes(k))
)
.filter(obj =>
Object.values(obj)
.map(v => v.toLocaleLowerCase())
.some(v => v.includes(searchValue.toLowerCase()))
)
props.search.onChange(filtered)
} else {
props.search.onChange(props.search.list)
}
}
Example
Let's assume props as:
const props = {
search: {
list: [
{ name: "John", title: 'Owner' },
{ name: "Jane", title: 'Admin' },
{ name: "Reza", title: 'Owner' }
],
onChange: x => console.log(x)
},
}
handleSearch("own")
// [ { name: 'John', title: 'Owner' }, { name: 'Reza', title: 'Owner' } ]
handleSearch("jane")
// [ { name: 'Jane', title: 'Admin' } ]
handleSearch("something")
// []

Loop over array of objects, remove duplicates, take one out and assign boolean to original

I have the following sample arr:
const fetchedArr = [
{ id: "3cc74658-a984-4227-98b0-8c28daf7d3d4", type: a },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: a },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: b }
]
i need the following output :
const arr = [
{ id: "3cc74658-a984-4227-98b0-8c28daf7d3d4", type: a, checked: true },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: a, checked: true, hasPair: true }
]
I have the following snippet which works
const newLegendItems = fetchedArr
.reduce((acc, curr, idx, arr) => {
const singleComponentLines = arr.filter((g) => g.id === curr.id);
const exists = !!acc.find((x) => x.id === curr.id);
if (!exists) {
if (singleComponentLines.length === 2 && singleComponentLines.includes(curr)) {
acc[idx] = {...curr, hasPair: true};
} else {
acc[idx] = curr;
}
}
return acc;
}, [])
.map((l) => ({ ...l, checked: true }));
, but i was thinking if there's simpler way to achieve this?
I should clarify that in the fetchedArr, the type does not matter, and that there won't be more than two same Id's, hence my idea for singleComponentLines.length === 2.
Like this?
const fetchedArr = [
{ id: "3cc74658-a984-4227-98b0-8c28daf7d3d4", type: "a" },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: "a" },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: "b" }
];
let result = fetchedArr.reduce((acc,v) => {
//first i need to check if i already have an element with the same ID in my accumulator. i either get -1 for not found or the index where the element is.
let i = acc.findIndex(el => el.id === v.id);
if(i !== -1) {
//if there is an element then access the element in the array with a[i] and add a new property to the object with ["hasPair"] and set it to true
acc[i]["hasPair"] = true;
return acc;
}
//in case i = -1 what means not found
return [...acc, {...v, checked: true}];
},[])
console.log(result);
I don't fully understand your question but it should help:
const result = [{
id: "3cc74658-a984-4227-98b0-8c28daf7d3d4",
type: 'a'
},
{
id: "9b96e055-dc2a-418c-9f96-ef449e34db60",
type: 'a'
},
{
id: "9b96e055-dc2a-418c-9f96-ef449e34db60",
type: 'b'
}
].reduce((acc, el) => {
const idx = acc.findIndex(it => it.id === el.id);
if (idx > -1) {
acc[idx] = { ...acc[idx],
hasPair: true
}
} else {
acc.push({ ...el,
checked: true
});
}
return acc;
}, []);
console.log(result)
I rather use a Map for this kind of things since it brings more readability IMO.
Start by checking if we already have it
Update our component and add it to the Map
The only "tricky" thing is that we need to iterate over .values() to grab our updated components, but thanks to spread operator it's quite easy.
const components = [
{ id: "3cc74658-a984-4227-98b0-8c28daf7d3d4", type: 'a' },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: 'a' },
{ id: "9b96e055-dc2a-418c-9f96-ef449e34db60", type: 'b' },
];
const newLegendItems = components
.reduce((acc, component) => {
if (acc.has(component.id)) {
acc.get(component.id)['hasPair'] = true;
} else {
acc.set(component.id, { ...component, checked: true });
}
return acc;
}, new Map());
console.log([...newLegendItems.values()]);

How to find a string from multiple objects?

I'm trying to update a state in my redux reducer by passing the deleted item id. The Id is located in tasks and associated column. What would a clean way to delete this item?
So far my reducer looks like this:
case DELETE_TASK:
const update = delete state.tasks[`${action.payload.id}`]
const findIdCol = ?
return {
}
const initState = {
tasks: {
"task1": {
id: "task1",
content: "hello1"
},
"task2": {
id: "task2",
content: "hello2"
},
"task3": {
id: "task2",
content: "hello3"
}
},
columns: {
"column1": {
id: "column1",
taskIds: []
},
"column2": {
id: "column2",
taskIds: []
},
"column3": {
id: "column3",
taskIds: ["task3", "task1"]
}
},
main: {
"main": {
id: "main",
taskIds: ["task2"]
}
},
columnOrder: ["column1", "column2", "column3"],
mainOrder: ["main"]
};
You can convert the columns object into its entries, allowing you to loop over each entry You can recreate the columns object, using reduce:
state.columns = Object.entries(state.columns).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== action.payload.id)
a[k] = v
return a
}, {})
Demo:
const state = {
tasks: {
task1: { id: 'task1', content: 'hello1' },
task2: { id: 'task2', content: 'hello2' },
task3: { id: 'task2', content: 'hello3' }
},
columns: {
column1: { id: 'column1',taskIds: [] },
column2: { id: 'column2',taskIds: [] },
column3: { id: 'column3',taskIds: ['task3', 'task1'] }
},
main: {
main: { id: 'main', taskIds: ['task2']}
},
columnOrder: ['column1', 'column2', 'column3'],
mainOrder: ['main']
}
const id = 'task1'
state.columns = Object.entries(state.columns).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== id)
a[k] = v
return a
}, {})
console.log(state)
You would replace id with action.payload.id in this case.
If you wanted to reuse this code, you could make it into a function:
const state = {
tasks: {
task1: { id: 'task1', content: 'hello1' },
task2: { id: 'task2', content: 'hello2' },
task3: { id: 'task2', content: 'hello3' }
},
columns: {
column1: { id: 'column1',taskIds: [] },
column2: { id: 'column2',taskIds: [] },
column3: { id: 'column3',taskIds: ['task2', 'task1'] }
},
main: {
main: { id: 'main', taskIds: ['task2']}
},
columnOrder: ['column1', 'column2', 'column3'],
mainOrder: ['main']
}
const id = 'task2'
const removeId = (o, id) => {
return Object.entries(o).reduce((a, [k, v]) => {
v.taskIds = v.taskIds.filter(taskId => taskId !== id)
a[k] = v
return a
}, {})
}
state.columns = removeId(state.columns, id)
state.main = removeId(state.main, id)
console.log(state)
To keep your reducer function pure and state immutable, I would not recommend to update the properties of the state directly (neither with reduce() returning an object, nor with delete operator). The cleanest approach here would be to follow the pattern
grab the state
put it into variable
modify relevant parts
replace the original state with its modified copy
That would give you something, like this (e.g. if you wish to remove task3 from your default state):
//dependencies
const { createStore } = Redux
//store initialization
const defaultState = {tasks:{"task1":{id:"task1",content:"hello1"},"task2":{id:"task2",content:"hello2"},"task3":{id:"task3",content:"hello3"}},columns:{"column1":{id:"column1",taskIds:[]},"column2":{id:"column2",taskIds:[]},"column3":{id:"column3",taskIds:["task3","task1"]}},main:{"main":{id:"main",taskIds:["task2"]}},columnOrder:["column1","column2","column3"],mainOrder:["main"]},
appReducer = (state=defaultState, action) => {
switch(action.type){
case 'DELETE_TASK': {
//destructuring state into parts to be modified
const {tasks,columns,main} = state,
{payload: taskIdToDelete} = action
//iterate through tasks to delete matching id's
Object.entries(tasks).forEach(([key, {id}]) => id == taskIdToDelete && delete tasks[key])
//iterate through columns to filter out deleted task
Object.entries(columns).forEach(([key, {taskIds}]) => columns[key]['taskIds'] = taskIds.filter(taskId => taskId != taskIdToDelete))
//filter out deleted task from main
main.main.taskIds = main.main.taskIds.filter(taskId => taskId != taskIdToDelete)
return {...state, tasks, columns, main}
}
default: return state
}
},
store = createStore(appReducer)
//dispatch 'DELETE_TASK'
store.dispatch({type:'DELETE_TASK', payload: 'task3'})
//log modified state
console.log(store.getState())
.as-console-wrapper{min-height:100%}
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.4/redux.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.1/react-redux.min.js"></script><div id="root"></div>

Combining two functions for working with an array of objects

I have two arrays of objects.
const details = [
{
ciphertext: 1234,
buyer: {
op_timezone: 7689,
op_city: 'Name1',
},
assignment_info: {
info: {
end_data: 1456,
start_date: 2389,
}
}
},
{
ciphertext: 5678,
buyer: {
op_timezone: 4568,
op_city: 'Name2',
},
assignment_info: {
info: {
end_data: 3467,
start_date: 8753,
}
}
},
];
const jobIds = [
{
id: 1,
},
{
id: 2,
},
];
I need to combine two arrays and take the assignment_info.info and buyer fields from each object.
function getDetailsBuyersWithJobIds(jobIds, details) {
return jobIds.map((item, index) => ({
...item,
...details[index].buyer,
}));
};
function getDetailsAssignmentInfosWithJobIds(jobIds, details) {
return jobIds.map((item, index) => ({
...item,
...details[index].assignment_info.info,
}));
};
The question is, how can two functions be combined into one?
That there would be no duplicate function, since they perform the same thing.
You can do a generic mapping function and pass it a getter function that will be able to fetch the proper data, not sure it will help the global readibility though.
What do you think about that?
const genericMapper = (getter) => (item, index) => ({
...item,
...getter(details[index]),
});
function getDetailsBuyersWithJobIds(jobIds, details) {
return jobIds.map(genericMapper(it => it.buyer));
};
function getDetailsAssignmentInfosWithJobIds(jobIds, details) {
return jobIds.map(genericMapper(it => it.assignment_info.info));
};
const details = [
{
ciphertext: 1234,
buyer: {
op_timezone: 7689,
op_city: 'Name1',
},
assignment_info: {
info: {
end_data: 1456,
start_date: 2389,
}
}
},
{
ciphertext: 5678,
buyer: {
op_timezone: 4568,
op_city: 'Name2',
},
assignment_info: {
info: {
end_data: 3467,
start_date: 8753,
}
}
},
];
const jobIds = [
{
id: 1,
},
{
id: 2,
},
];
console.log(getDetailsBuyersWithJobIds(jobIds, details));
console.log(getDetailsAssignmentInfosWithJobIds(jobIds, details));
You can add values on return object based on condition something like this
const details = [{ciphertext: 1234,buyer: {op_timezone: 7689,op_city: 'Name1',},assignment_info: {info: {end_data: 1456,start_date: 2389,}}},{ciphertext: 5678,buyer: {op_timezone: 4568,op_city: 'Name2',},assignment_info: {info: {end_data: 3467,start_date: 8753,}}},];
const jobIds = [{id: 1,},{id: 2,},];
function getDetails(jobIds, details, props = {
getBuyer: true
}) {
return jobIds.map((item, index) => ({
...item,
...(props.getBuyer && { ...details[index].buyer
}),
...(props.getAssignment && { ...details[index].assignment_info.info
})
}));
};
console.log(getDetails([1], details, {
getBuyer: true
}))
console.log(getDetails([1], details, {
getAssignment: true
}))
Here props = { getBuyer: true} used to set a default value.

Categories