How to Copy Part of json? - javascript

I get information from github's api, but some of the returned data I don't want.The following is my code for filtering json data.
However, it is too complicated to write every field. Is there a more convenient way to write it?
result => {
var obj2 = {
items: []
}
function ObjectCreate(id, tags, score, link, title) {
this.title = title
this.link = link
this.score = score
this.id = id.toString();
this.tags = tags;
}
var need = results.items
for (var i = 0; i < results.items.length; i++) {
var obj = new ObjectCreate(i, need[i].tags, need[i].score, need[i].link, need[i].title);
obj2.items.push(obj);
}
let str = JSON.stringify(obj2, "", "\t")
}

You can use destructing assignment
let data = [{'id':120,'title': 'hello','link': '#', 'score': 24,'tags': 'tags blah blah','location': 'US','price': 120,},{'id':12,'title': 'hello wprld', 'link': '#','score': 125,'tags': 'tags blah blah', 'location': 'SO','price': 12,}
]
const format = data.map( ({id,link,title,score,tags}, index) => ({id:index,link,title,score,tags}))
console.log(format)

Slightly shorter:
const format = ({ items }) => ({
items: items.map((el, id) => ({ id, title: el.title, link: el.link, score: el.score, tags: el.tags }))
});
Or using some helper functions:
const lens = (key, fn) => obj => ({ ...obj, [key]: fn(obj[key]) });
const pick = (...keys) => obj => Object.assign(...keys.map(k => ({ [k]: obj[k] })));
const map = (...fns) => arr => arr.map((el, i) => fns.reduce((el, fn) => fn(el, i), el));
const format = lens("items",
map(
pick("title", "link", "score", "tags"),
(el, index) => el.id = index
)
);

Related

React - How to convert a nested object into a array

I have the following object that I would like to convert it into an array of object and each object from the array to have the key: value[0].
object:
const obj = {
comedy: ['book1', 'book2'],
action: ['book3', 'book4'],
drama: ['book5'],
}
output
const arr = [
{comedy: 'book1'},
{comedy: 'book2'},
{action: 'book3'},
{action: 'book4'},
{drama: 'book5'},
]
So far I have tried solving this with loops and using Object.entries. But I do not get the output I need.
let result = [];
for(const [key1, value1] of Object.entries(arr)) {
for (const [key2, value2] of Object.entries(value1)) {
if(result[key2]) {
result[key2] [key1] = value2
} else {
result[key2] = {[key1]: value2}
}
}
}
console.log(result)
I have also tried to loop recalling the function inside itself. (I have found this search for the answer). But it will get stucked in a infinite loop.
const result = [];
const getArray = (obj) => {
if (!obj) return;
const {keys, ...rest} = obj;
result.push({...rest});
getArray(keys);
}
console.log('here', getArray(arr));
You could use a reduce over the entries of obj, adding objects to the accumulator for each entry in the array value:
const obj = {
comedy: ['book1', 'book2'],
action: ['book3', 'book4'],
drama: ['book5'],
}
const arr = Object.entries(obj)
.reduce((acc, [k, v]) => acc.concat(v.map(b => ({ [k] : b }))),
[]
)
console.log(arr)
Try this
function nestedObjectIntoArray(obj) {
const arr = [];
for (const key in obj) {
for (const value of obj[key]) {
const obj = {};
obj[key] = value;
arr.push(obj);
}
}
return arr;
}
const obj = {
comedy: ["book1", "book2"],
action: ["book3", "book4"],
drama: ["book5"],
};
console.log(nestedObjectIntoArray(obj));
const obj = {
comedy: ['book1', 'book2'],
action: ['book3', 'book4'],
drama: ['book5'],
}
const result = Object
.keys(obj)
.reduce((acc, key) =>
[...acc, ...obj[key]
.map((value) => ({[key]: value}))],
[]
)
console.log(result)
We can also achieve this by just using Array.forEach() method.
Live Demo :
// Input object
const obj = {
comedy: ['book1', 'book2'],
action: ['book3', 'book4'],
drama: ['book5'],
};
// Declare a variable to store the final result.
const arr = [];
// Iterate the input object based on the keys and build the final object.
Object.keys(obj).forEach(key => {
obj[key].forEach(item => {
arr.push({ [key]: item })
})
});
// Result
console.log(arr);
we can achieve this way too
const obj = {
comedy: ['book1', 'book2'],
action: ['book3', 'book4'],
drama: ['book5'],
}
const fun = (ar, temp)=>{
for(x in ar){
ar[x].map((e, i)=> {
var data = {[x] : e}
temp.push(data)
})}
return temp
}
console.log(fun(obj , []))

Run async/await function inside a reduce Javascript [duplicate]

This question already has answers here:
JavaScript array .reduce with async/await
(11 answers)
Closed 6 months ago.
I need to fetch values from another API using the guid inside this particular array, then group them together (hence I used reduce Javascript in this case)
However, I could not get those values sumEstimatedHours and sumWorkedHours as expected. Can someone suggest a method please?
export const groupProjectsByPM = (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = listOfProjects?.reduce(
(
group,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
here is an example of async/await inside reduce:
let's assume that we have an array of numbers
const arrayOfNumbers = [2,4,5,7,6,1];
We are going to sum them using reduce function:
const sumReducer = async () => {
const sum = await arrayOfNumbers.reduce(async (promisedSum, num) => {
const sumAcc = await promisedSum
// any promised function can be called here..
return sumAcc + num
}, 0)
console.log(sum)
}
So the trick is to remember to await the accumulator inside the reduce function
export const groupProjectsByPM = async (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = await listOfProjects?.reduce(
async (
promisedGroup,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
//accumulator in your case is group
const group = await promisedGroup;
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
Best of luck ...

Comparing two array of objects and getting the difference

Hi I'm trying to show a loader for each input field, but only when they have entered a value. The behaviour i'm getting is if i enter a value into one of the text inputs the spinner shows for all. I need to determine my loading state based on the difference of the two arrays, by the array key.
Similar to this post -> How to get the difference between two arrays of objects in JavaScript
const [isLoading, setIsLoading] = useState('');
// before any value has been entered
const initialFormData = [{key: 'USER_DEFINED_QUESTIONS', value: ''}, {key: 'RECOMMENDED_FIELDS', value: ''}]
// when a value has been entered
const values = [{key: 'USER_DEFINED_QUESTIONS', value: 'test'}, {key: 'RECOMMENDED_FIELDS', value: ''}]
const saveSubmit = async (values) => {
const arrValues = Object.entries(values).map((val) => ({
field: val[0],
value: val[1],
}));
const arrInitialFormValues = Object.entries(initialFormData).map(
(val) => ({
field: val[0],
value: val[1],
}),
);
const result = arrValues.filter(
(o1) => !arrInitialFormValues.some((o2) => o1.key === o2.key),
);
const body = { ...values };
setIsLoading(result);
const res = await putResults({ id, body });
if (res && !(await checkErrors(res))) {
return res;
}
setIsLoading(result);
}
return null;
};
You can combine the renaming and filter
const saveSubmit = async (values) => {
const result = initialFormData.reduce((acc, a) => {
const diff = values.find((b) => a.key === b.key && a.value !== b.value)
return diff
? [
...acc,
{
field: diff.key,
value: diff.value,
},
]
: acc
}, [])
const body = { ...values }
setIsLoading(result)
const res = await putResults({ id, body })
if (res && !(await checkErrors(res))) {
return res
}
setIsLoading(result)
}
First of all, I would map the array into an object (this is not needed, it just feels cleaner this way).
values = Object.fromEntries(a.map(entry => [entry.key, entry.value]))
Then I believe, all you really need is to find all entries that have non-negative "value" (if that is not the case, please comment below and I will correct this answer).
usedFields = Object.entries(values).filter(entry => entry[1])
If you want only to get the difference, and that is what i can read from question title, shouldn't this work?
const initialFormData = [{key: 'USER_DEFINED_QUESTIONS', value: ''}, {key: 'RECOMMENDED_FIELDS', value: ''}];
// when a value has been entered
const values = [{key: 'USER_DEFINED_QUESTIONS', value: 'test'}, {key: 'RECOMMENDED_FIELDS', value: ''}];
const keysWhichDiffer = values.map(x => initialFormData.find(y=> x.key===y.key && x.value!==y.value)?.key);
const result = keysWhichDiffer.filter(x=>x);
console.log('res', result)

Call a function with many arguments in JavaScript

I have a compose function what should be called with n number of function, depending by what function was added as parameter.
For example:
const props = ['link', 'letter'];
const mapp = {
letter: (v) => {
v + 'letter'
},
link: (v) => {
v + 'nr'
}
}
const compose = (...fns) =>
fns.reduce(
(prevFn, nextFn) =>
(...args) =>
nextFn(prevFn(...args)),
(value) => value,
);
const res = compose(props);
console.log(res('test'))
So, i expect, if the const props = ['link', 'letter']; the compose function should be called like: const res = compose(mapp[letter], mapp[link]);, or if const props = ['letter'];, the compose function should be called as: compose(mapp[letter]). At the moment the code does not work as expeted. Question: How to fix the code and to get the expected result?
There was 2 problems with your code
Missing return statement(s) in the mapp object's functions (or remove the { and } - see below)
You needed to map the string array into references to the functions and spread (...) the result when calling compose
Working example:
const props = ['link', 'letter'];
const mapp = {
letter: (v) => {
return v + 'letter'
},
link: (v) => {
return v + 'nr'
}
}
const compose = (...fns) =>
fns.reduce(
(prevFn, nextFn) => (...args) => nextFn(prevFn(...args)),
(value) => value,
);
const res = compose(...props.map(p => mapp[p]));
console.log(res('test'))
Regarding 1. above that object could be written as below:
const mapp = {
letter: (v) => v + 'letter',
link: (v) => v + 'nr'
}
Edit: The need to destructure the call to map is because of the way you defined compose to take discrete parameters rather than an array. ie, this would also work (and not need to destructure map)
const props = ['link', 'letter'];
const mapp = {
letter: (v) => {
return v + 'letter'
},
link: (v) => {
return v + 'nr'
}
}
const compose = (fns) => /// compose now takes an array of functions
fns.reduce(
(prevFn, nextFn) => (...args) => nextFn(prevFn(...args)),
(value) => value,
);
const res = compose(props.map(p => mapp[p]));
console.log(res('test'))
I do not see how you are using the object with the functions and you are not returning correctly in a few places. It should look something like
const props = ['link', 'letter'];
const mapp = {
letter: (v) => v + 'letter',
link: (v) => v + 'nr',
}
const compose = (obj, fns) => (...orgArgs) =>
fns.reduce((args, fnName) => obj[fnName].apply(obj, [args]), orgArgs);
const res = compose(mapp, props);
console.log(res('test'))

Is there a better way to achieve this?

I am using React. On click of a button, the following function is executed:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const updatedItem = updatedData.filter((ele) => ele.id === idValue)[0];
updatedItem.completed = true;
const newData = updatedData.filter((ele) => ele !== updatedItem);
newData.unshift(updatedItem);
return newData;
});
};
My data is an array of objects like this:
[{userId: 1, id: 2, title: "task 1", completed: true}, .....].
Basically I want to move the updated item to the start of the array. Is there any better solution for this?
updatedItem should not be mutated. And this string const newData = updatedData.filter((ele) => ele !== updatedItem); is not fine. You can do it like this :
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetItem = prevData.find((ele) => ele.id === idValue);
const updatedItem = { ...targetItem, completed: true };
const filteredData = prevData.filter((ele) => ele.id !== idValue);
return [updatedItem, ...filteredData];
});
};
Even better to reducing an extra filter:
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const targetIndex = prevData.findIndex((ele) => ele.id === idValue);
return [{ ...prevData[targetIndex], completed: true }].concat(prevData.slice(0, targetIndex + 1)) .concat(
prevData.slice(targetIndex + 1)
)
});
};
First find index of updated element using Array.findIndex(), then remove the same element using Array.splice() and add it to front of the array.
const completeTaskHandler = (idValue) => {
setData((prevData) => {
const updatedData = [...prevData];
const index = updatedData.findIndex(obj => obj.id === idValue);
const [updatedItem] = updatedData.splice(index, 1);
updatedItem.completed = true;
updatedData.unshift(updatedItem);
return updatedData;
});
};
The simplest one with only one forEach.
const completeTaskHandler = idValue => {
setData(prevData => {
let updatedItem = {}, newData = [];
prevData.forEach((ele) => {
if (ele.id === idValue) {
updatedItem = ele;
updatedItem.completed = true;
} else {
newData.push(ele);
}
});
newData.unshift(updatedItem);
return newData;
});
};

Categories

Resources