Comparing two array of objects and getting the difference - javascript

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)

Related

How to join two arrays in a map function?

What I want is to join my arrays that I get from my map function.
I tried to do with reduce :
const onFinish = (values) => {
values.fields.map((el, idx) => {
console.log({ ...el, index: idx }); // this `console.log` prints me one array after the other
}).reduce((acc, x) => {console.log(acc,x)}); // I tried with `reduce` but it prints me null
Also tried to do with useState:
const onFinish = (values) => {
values.fields.map((el, idx) => {
if (myArray === null){
setMyArray(() => {
return { ...el, index: idx };
});
}
else {
setMyArray(() => {
return {...myArray,...el, index: idx };
});
}
});
console.log(myArray) // at my first call to this const onFinish myArray is printed as [] and at the second it prints with the last object only
};
Someone knows how to get these objects joined/merged ?
Sample data the way that it's print:
How I want to be:
Altghough it is still not very clear from the screenshots (always prefer to use text and not images for data/code), it looks like you want
const onFinish = (values) => {
const updatedValues = values.fields.map((field, index) => ({...field, index}));
console.log( updatedValues );
}
Does this help?
if (myArray === null){
setMyArray(() => {
return [{ ...el, index: idx }];
});
}
else {
setMyArray(() => {
return [...myArray, {...el, index: idx }];
});
}

Creates an object composed of the picked object properties: [duplicate]

This question already has answers here:
Filter object properties by key in ES6
(30 answers)
Closed 1 year ago.
I'm trying to create an object from object and list of properties.
const pick = (obj, ...fields) => {
return [...fields] = obj
};
How can I realise this?
Reduce the list of fields, and take the values from the original object:
const pick = (obj, ...fields) => fields.reduce((acc, field) => ({ ...acc, [field]: obj[field] }), {});
const obj = { a: 1, b: 2, c: 3 };
const result = pick(obj, 'a', 'c');
console.log(result);
You can use the in operator to ignore properties that don't exist on the original object:
const pick = (obj, ...fields) => fields.reduce((acc, field) => {
const value = obj[field];
if(field in obj) acc[field] = value;
return acc;
}, {});
const obj = { a: 1, b: 2, c: 3 };
const result = pick(obj, 'a', 'c', 'd');
console.log(result);
Try something like this:
const pick = (obj, ...fields) => Object.fromEntries(Object.entries(obj).filter(([k]) => fields.includes(k)))
Iterate through fields array and check if property is available in obj then put into final object which needs to be returned.
const pick = (obj, ...fields) => {
const finalObj = { };
for (field of fields) {
if (obj[field]) {
finalObj[field] = obj[field];
}
}
return finalObj;
};
const obj = { name: "test name", age: 25, title: "Mr.", city: "test city" };
console.log(pick(obj, "name", "age", "city", "other"));

Transform API data and fetching every 60 seconds [react]

Hi I'm currently having problems with formatting data I'm receiving from an API
Should I use reduce instead of forEach?
Here is the desired output, an array of objects with just the titles and prices:
[
{
title: "Meter",
price:"9.99"
},
{
title: "Plan One",
price:"11.99"
}
]
Here is how I am getting the data, setting it, refetching it every 60 seconds:
const [products, setProducts] = useState([]);
const formatProduct = (products) =>{
let result = [];
products.forEach((product)=>{
if(product.variants['price'] !== "0.00"){
result.push({product["title"],product.variants[0]["price"]})
}
})
return result
}
useEffect(() => {
const fetchData = async () => {
const axiosResponse = await axios(`https://www.today/products.json`);
setProducts(formatProduct(axiosResponse.data.products));
};
const reFetchData = setInterval(() => setProducts(fetchData()), 60000);
return () => {
clearInterval(reFetchData);
};
}, []);
Adding object to the result array you have to specify the name of props. Also you've missed index of variants when you compare a price:
const formatProduct = (products) =>{
let result = [];
products.forEach((product)=>{
if(product.variants[0]['price'] !== "0.00"){ //index 0
result.push({
title: product["title"], // specify the prop name
price: product.variants[0]["price"]
})
}
})
return result
}
There is another problem in your code. What happens if variant doesn't contain any item? I would check array length and use filter and map functions:
const formatProduct = (products) => {
return products
.filter(p => p.variants.lenght>0 && p.variants[0]!=="0.00")
.map(p => {title: p.title, price: p.variants[0].price});
}
Another problem is how you call setProducts(fetchData()) in the interval function. You shouldn't call setProducts() because you're already calling it in fetchData(). So it has to be:
const reFetchData = setInterval(() => fetchData(), 60000);

React : Need a faster way to filter data and setState

I need to make a menu list for a restaurant app, and the menu data is categorized in American, Chinese, Indian, Italian. I need to loop over all these to render it somehow in the scrollspy type menu.
For that I have configured the backend to send in ALL the ITEMS at once and need to filter and sort it as per the category on the react side.
DATA STRUCTURE :
{
_id: 5eef61450bd95e1f5c8f372f
name: "Burger"
category: "American"
price: "100"
isVeg: false
__v: 0
}
The way i am doing is seems too slow and I believe there is GOT TO BE A FASTER/EFFECIENT WAY. Please suggest, because my way makes me want to puke.
const CheckForms = () => {
const [american, setAmerican] = useState([]);
const [italian, setItalian] = useState([]);
const [indian, setIndian] = useState([]);
const [chinese, setChinese] = useState([]);
const fetchList = async () => {
try {
const res = await axios.get(`http://localhost:5000/api/items`);
const list = res.data.response;
let ch = [];
let ind = [];
let am = [];
let it = [];
list.forEach(function(each){
if (each.category === "Chinese") ch.push(each)
else if (each.category === "Indian") ind.push(each)
else if (each.category === "American") am.push(each)
else if (each.category === "Italian") it.push(each)
else console.log('undefined category');
});
setAmerican(am);
setIndian(ind);
setChinese(ch);
setItalian(it);
} catch (err) {
console.log(err.response);
};
};
useEffect(()=> {
fetchList();
}, []);
let render;
if (indian.length > 0 && american.length > 0 && chinese.length > 0 && italian.length > 0) {
render = (
/*********************************
* AND FURTHER RENDERING LOGIC :(
********************************/
);
};
You could try reduce:
const list = [
{ category: 'Chinese', name: 'one-1' },
{ category: 'Chinese', name: 'one-2' },
{ category: 'Indian', name: 'two' },
];
const groups = list.reduce(
(result, item) =>
result.set(
item.category,
(result.get(item.category) || []).concat(item)
),
new Map()
);
console.log('Chinese',groups.get('Chinese'));
console.log('Indian',groups.get('Indian'));

Check if every id has an existing object in array

I would like to check if there is an existing object for every id of an array.
const ids = [ 'xxnQt5X8pfbcJMn6i', 'fbcJMn6ixxnQt5X8p' ]
const target = [
{ _id: 'xxnQt5X8pfbcJMn6i' },
{ _id: 'Qt5X8pfbcJMn6ixxn' },
]
In this example I would like to get false, as the second ID (fbcJMn6ixxnQt5X8p) is not existing.
This should return true:
const ids = [ 'xxnQt5X8pfbcJMn6i', 'fbcJMn6ixxnQt5X8p' ]
const target = [
{ _id: 'xxnQt5X8pfbcJMn6i' },
{ _id: 'Qt5X8pfbcJMn6ixxn' },
{ _id: 'fbcJMn6ixxnQt5X8p' },
]
This is what I've tried:
ids.every(id => target.find(element => element._id === id))
You're pretty close, you need to pass a function instead of an object to find though - and I'd recommend to use some() instead of find if you only want to know about if it exists, rather than you need the object in return.
const ids = [ 'xxnQt5X8pfbcJMn6i', 'fbcJMn6ixxnQt5X8p' ]
const target = [
{ _id: 'xxnQt5X8pfbcJMn6i' },
{ _id: 'Qt5X8pfbcJMn6ixxn' },
{ _id: 'fbcJMn6ixxnQt5X8p' },
]
const allIn = ids.every(id => target.some(({_id}) => id === _id));
console.log(allIn);
With a lot of entries, it might be faster to use a Set (so we get O(n + m) instead of O(n * m)) :
const idSet = new Set(target.map(el => el._id));
const result = ids.every(id => idSet.has(id));
Since every is itself a checker method it's return true or false through the provided function. So you can simply return false:
const res = const res = ids.every(id => target._id === id);
console.log(res);
or true:
const res = ids.every(id => target._id !== id);
console.log(res);

Categories

Resources