bug from element showing - javascript

Hi all I have following code: my code
In code I am recieving data from backend, In my case I hardcoded that part (see below data)
const skills = [
{
id: 1,
name: "Html "
},
{ id: 2, name: "CSS" },
{ id: 3, name: "Scss" },
{ id: 4, name: "Bootstrap 4" },
{ id: 5, name: "JavaScript" },
{ id: 6, name: "Jquery" },
{ id: 7, name: "React JS" },
{ id: 8, name: "Angular " },
{ id: 9, name: "Vue.js " },
{ id: 10, name: "SQL" },
{ id: 11, name: "php" },
{ id: 12, name: "Laravel" }
];
I am counting all my skill names char length and if that length is greater then allowCharCount I am hiding all rest skills and showing in number how many skills is hided. That part is working.
let lengthCount = 0;
let maxIndex = 0;
const allowCharCount = 20;
const skill = [];
export const Skill = ({ data }) => {
if (data === undefined) {
return null;
} else {
data.map((item) => {
if (lengthCount <= allowCharCount) {
maxIndex = item.id;
skill.push(item);
}
lengthCount += item.name.length;
});
const mySkills = skill.map((perSkill) => (
<span key={perSkill.id} className="skillItem">
{perSkill.name}
</span>
));
return (
<div className="skillWrapper">
<div>{mySkills}</div>
{lengthCount > allowCharCount ? (
<div className="skillNumber">+{data.length - maxIndex}</div>
) : null}
</div>
);
}
};
but when my chars count is less then allowCharCount it's not working.
If I only have first 3 items (Html, CSS, Scss) I see following view
Html CSS Scss Html CSS Scss +0.
Please help me to fix this code for that case (if chars count is less than allowCharCount) I need to show only correct items with no any +0 count

You should rethink the way you write the component and code in general
You have to store dynamic variables inside the component
To save the result of array operations, you should either mutate state or create a new variable inside the component (which is more preferable in your case).
.map method return an array of values returned by callback inside of it. If you're not going to return anything, use .forEach instead
Use .length property instead of incrementing the size of array by yourself to avoid bugs.
The reason why you get duplicated elements is that you don't clear the array before the component updates, so the algorithm just pushes these values again.
Working code example:
export const Skill = ({ data }) => {
// here I invert condition to get the rest code out of brackets
if (!data) return null;
// all of the dynamic data should be inside the component
const failIndex = React.useMemo(() => {
let charCount = 0;
for (let i = 0; i < data.length; i++) {
charCount += data[i].name.length;
if (charCount > allowCharCount) return i;
}
return -1;
}, [data]);
// check if data has element that doesn't pass the condition
const dataBeforeFail = failIndex === -1 ? data : data.slice(0, failIndex + 1);
const hiddenSkillsCount = data.length - dataBeforeFail.length;
return (
<div className="skillWrapper">
<div>
{dataBeforeFail.map((perSkill) => (
<span key={perSkill.id} className="skillItem">
{perSkill.name}
</span>
))}
</div>
{hiddenSkillsCount > 0 && (
<div className="skillNumber">+{hiddenSkillsCount}</div>
)}
</div>
);
};

Related

find duplicate values in array of objects in javascript

I have two lists in javascript that are of same structure like below:
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
I need to remove all records from database that are in existing documents list (i.e "dt") but NOT in required_documents list.
For the above scenario I should remove only {"id":3,"dt":4} and insert {"id":3,"dt":3}. I am not sure how I can compare on just one property. This is below that I found on SOF sometime ago but can't find it again apologies for not referencing it.
required_documents.forEach((obj) => {
const elementInArr2 = existing_documents.find((o) => o.dt === obj.dt);
console.log('found elementinarr: ' + obj.dt);
});
This returns unique objects like dt:1,dt:2,dt:3 but I need dt:4 from the existing documents list as it is the one that is not in the required documents list and needs to be deleted. How can I get just the one that is not in the required documents list.
Assuming both id and dt properties are significant, I would first create a means of hashing an entry and then build a hashed set of required_documents.
Then you can filter out anything from existing_documents that is in the set, leaving only the results you want.
const required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
const existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
// a simple stringify hash
const createHash = ({ id, dt }) => JSON.stringify({ id, dt });
const requiredHashSet = new Set(required_documents.map(createHash));
const result = existing_documents.filter(
(doc) => !requiredHashSet.has(createHash(doc))
);
console.log(result);
The hash creation can be anything that produces a comparable entity that can uniquely identify a record.
You need to run it twice to confirm there is no elements left in existing. So create a function and use it.
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}]
let output = [];
output = output.concat(extractUniqueValues(required_documents, output));
output = output.concat(extractUniqueValues(existing_documents, output));
console.log(output)
function extractUniqueValues(input, output){
return input.filter((item)=>{
return !output.find(v => v.dt == item.dt)
})
}
You can do like below
var required_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 3 },
];
var existing_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 4 },
];
for (let index = 0; index < required_documents.length; index++) {
const element = required_documents[index];
for (var i = existing_documents.length - 1; i >= 0; i--) {
const child = existing_documents[i];
if (element.id === child.id && element.dt === child.dt) {
existing_documents.splice(i, 1);
} else {
required_documents.push(element);
}
}
}
LOG not exist [{"dt": 4, "id": 3}]
LOG unique items [{"dt": 1, "id": 1}, {"dt": 2, "id": 2}, {"dt": 3, "id": 3}]
If you don't care about time complexity, something this should work:
var new_documents = existing_documents.filter(ed => {
return required_documents.find(rd => rd.dt == ed.dt);
});
Edit Okay, I just reread your question and I'm a bit confused. Do you want the object {id: 3, dt: 3} inside the new array as well?

Need to limit the amount of data returned from an API using Reactjs

I'm receiving some transaction data from an API. One of the items in this return is buyer which sometimes can be null. Therefore I'm omitting any buyers that have null. This creates a varying number of results. Ideally I would like to show 5 cards and if there is only 1 result, then I'll figure out some place holder for the 4 other cards. The problem is when I have more than 5 results because it will create an additional card. I'm trying to find a solution that will limit it to 5. I can't achieve this by altering the API filters because of this "buyer = null" condition. Is there anything that can be done using a styles file or any JS/React code that I can add that will allow me to max it out?
Currently I have not brought in the Card component and am working only with list items.
return (
<ul>
{highestBuy.map((result) => {
const {
buyer,
price: { amount, token_symbol },
} = result;
if (!highestBuy) {
return (
<div>
<Spinner />
</div>
);
}
if (buyer !== null) {
return (
<li>
{buyer}
{amount}
{token_symbol}
</li>
);
}
})}
</ul>
);
There's several ways of doing this but there a moderately simple version.
// test array of fewer than 5 items
const cardsA = [{
buyer: "Adam"
}, {
buyer: "Bill"
}, {
buyer: null
}];
// more than 5 items
const cardsB = [{
buyer: "Adam"
}, {
buyer: "Bill"
}, {
buyer: null
}, {
buyer: "Charlie"
}, {
buyer: null
}, {
buyer: "David"
}, {
buyer: "Ernie"
}, {
buyer: null
},
{
buyer: "Frank"
}
];
const makeFive = (cards) => {
// filter out the nulls
cards = cards.filter(card => card.buyer !== null);
// make a new array to hold the cards
const newCards = [];
// loop 5 times
for (let ix = 0; ix < 5; ix++) {
// push a card from the filtered stack or a placeholder
// card if the filtered stack is too small
newCards.push(cards[ix] || {
buyer: "default"
});
}
// return the new cards
return newCards;
}
console.log("cardsA", makeFive(cardsA));
console.log("cardsB", makeFive(cardsB));

After adding in Array element change oher element but not adding to array

i've got an array:
dataSet: [
{ name: "Имя1", image: "img.jpeg", author: "Александр Полтавченко", date: "21.02.2020", id: 1 },
{ name: "Имя2", image: "img.png", author: "Александр Полтавченко", date: "21.02.2020", id: 2 },
],
addedToCart: []
and here is the function which put value from dataSet to addedToCart according ID from props:
added = (id) => {
this.setState (( { addedToCart, dataList } )=>{
const newItem = dataList.filter(el=>el.id===id);
const testArr = [...addedToCart ];
const filteredATC = testArr.filter((item, el)=>{
if(addedToCart.indexOf(item)===el){
item.count++
return item, el
}
else {
return item
}
it is works well (only one element with count ++) but if click add to another element it is just change element in array (with correct count surprisingly).
How to put another element into addedToCart, just like
[
{el1},
{el2}
]
filter returns an array instead of the desired element, you should use find instead.
I believe you would desire an approach like this:
added = (id) => {
this.setState (( { addedToCart, dataList } ) => {
const newItem = dataList.find(el=> el.id === id);
const testArr = [...addedToCart ];
const filteredATCIndex = testArr.findIndex((_item, id) => newItem.id === id)
// if there is an added item
if (filteredATCIndex !== -1) {
const count = testArr[filteredATCIndex].count + 1
testArr[filteredATCIndex] = { ...testArr[filteredATCIndex], count }
return { addedToCart: testArr }
}
// for new item
const newItemAdded = { ...newItem, count: 1 }
testArr.push(newItemAdded)
return { addedToCart: testArr }
})
}
though this approach duplicates data, which is not desirable. I suggest you consider to change addedToCart to an object where key value pairs are the id and count respectively from added items. This way you would avoid duplicating data.
then your update state would look like:
added = (id) => {
this.setState (( { addedToCart } ) => {
const count = typeof addedToCart[id] === 'undefined' ? 1 : ++addedToCart[id]
return { addedToCart: { ...addedToCart, [id]: count } }
})
}

How to map through the array to generate dynamic form based on api response?

I am using React.js. I want to create a dynamic form based on some api response. Below are the conditions for creation of the form.
1.Form fields must be generated based on values from table1 and table2 key from entityTable array. sometime table1 and table2 key may contain multiple values. In that case I need to split the values based on comma(,) and consider them individual values.
Now, I need to compare those values received from table1 and table2 key with the keys of recievedData object. If there is match then I need to generate form field with initialValue of corresponding key's value.
The form field must be disabled based on the key values of table1_edit and table2_edit from entityTable array. If value is 0, the form field must be disabled.
Please visit the codesandbox link for the code.
What I have done so far
class Todo extends Component {
render() {
// Here is the API response
const data = {
recievedData: {
pan_number: "3213265",
gender: "M",
last_name: "45645",
pan_status: "VALID",
middle_name: "null",
rece_pan_num: "435353",
first_name: "464",
sent_pan_num: "546546",
pan_name: "some name",
pan_holder_title: "null"
},
questions: [],
entityTable: [
{
id: 1,
table1: "pan_number",
table2: "sent_pan_num,rece_pan_num,pan_status",
table1_edit: "1",
table2_edit: "0"
},
{
id: 2,
table1: "pan_name,first_name",
table2: "middle_name,last_name",
table1_edit: "1",
table2_edit: "0"
},
{
id: 3,
table1: "gender",
table2: "pan_holder_title",
table1_edit: "1",
table2_edit: "0"
}
]
};
const mainArray = [];
const givenData =
data.entityTable &&
data.entityTable.map(item => {
if (item.table1.includes(",")) {
let newArray = item.table1.split(",");
let itemProp = item.table1_edit;
mainArray.push({ table_one: newArray, itemProp: itemProp });
} else {
if (item.table1 === "" || item.table1 === null) {
return null;
} else {
let newArray = item.table1;
let itemProp = item.table1_edit;
mainArray.push({ table_one: newArray, itemProp: itemProp });
}
}
if (item.table2.includes(",")) {
let newArray = item.table2.split(",");
let itemProp = item.table2_edit;
mainArray.push({ table_two: newArray, itemProp: itemProp });
} else {
if (item.table2 === "" || item.table2 === null) {
return null;
} else {
let newArray = item.table2;
let itemProp = item.table2_edit;
mainArray.push({ table_two: newArray, itemProp: itemProp });
}
}
return mainArray;
});
return (
<div>
<form>
{Object.keys(data.recievedData).map((selec, index) => {
// Here how do I need to compare the values from givenData to check the match?
return <input placeholder={data.recievedData[selec]} />;
})}
</form>
</div>
);
}
}
First, I map through the entityTable array and pushed the required value to an array. Now I stuck in the comparison that need to be done with recievedData object. How do I have to loop through the givenData array to check the match?
I'm not using the exact data structures you need because I don't really want to have to figure out exactly everything you need to do, I just wanted to show you how you might conditionally render one element based on if it matches something from another array:
class Todo extends Component {
constructor(props) {
super(props);
this.state = {
recievedData: [{
id: 2,
editable: false,
value: 'x'
}, {
id: 3,
editable: true,
value: 'm'
}],
givenData: [{
id: 3,
value: 'r'
}]
};
this.renderItem = this.renderItem.bind(this);
}
renderItem(data, index) {
const name = `item_${index}`;
const matchingData = this.state.givenData.find(item => item.id === data.id);
const readOnly = !data.editable;
if (matchingData) {
return <input name={name} type="text" readOnly={readOnly} defaultValue={matchingData.value}/>
} else {
return <input name={name} type="text" readOnly={readOnly} defaultValue={data.value}/>
}
}
render() {
return (
<div>
<form>
{Object.keys(this.state.recievedData).map((selec, index) => {
// Here how do I need to compare the values from givenData to check the match?
return this.renderItem(selec, index);
})}
</form>
</div>
);
}
}
As you can see, you can use a function inside the class to return a component. Use this function inside your .map function. You'll probably want the inputs to have a key a well, probably based on the id

How to conditionally update properties of items within a collection in React?

I have a collection of items stored in state:
this.state = {
items: [
{ name: "foo", description: "a foo", index: 0 },
{ name: "bar", description: "a bar", index: 1 },
{ name: "herp", description: "a herp", index: 2 },
{ name: "derp", description: "a derp", index: 3 }
]
};
The index property represents the ordinal position of each item in the collection. At some point I need to re-order these items. For example, "derp" may need to be moved to the front, so the indices of the other items need to be updated:
{ name: "derp", description: "a derp", index: 0 },
{ name: "bar", description: "a bar", index: 1 },
{ name: "herp", description: "a herp", index: 2 },
{ name: "foo", description: "a foo", index: 3 }
I am currently updating the state using update from the immutability-helper package. However, I am certain this is not the correct way to do it (although it works):
// originalIndex is a variable holding the original index
// newIndex is a variable holding the new index
// initialise updatedItems so we can update within the loop
let updatedItems = update(this.state.items, { [originalIndex]: {'index': {$set: newIndex}}});
for (var i = newIndex; i < this.state.items.length; i++) {
if (i !== originalIndex) {
updatedItems = update(updatedItems, { [i]: {'index': {set$: parseInt(this.state.items[i].index) + 1}}});
}
}
This feels like a massive hack.
My question is, is it possible to call update with conditional logic, and so can this loop be replaced with a single call to update?
Assuming that we pull the index property out of each item, you can create the new list like this:
const items = this.state.items.slice();
const value = items[oldIndex];
items.splice(oldIndex, 1); // remove the one you want to move
items.splice(newIndex, 0, value); // add it back to the desired index
this.setState({ items });
That is, use slice to make a (shallow) copy of the list, then use splice to swap the elements around.
Since you're only moving one element at a time, you can save a line using:
const [value] = items.splice(oldIndex, 1);
This assigns the first element of the array returned by splice to value.
If you want to keep index (why?), then you need to reassign the indices:
this.setState({ items: items.map((item, index) => ({ ...item, index })) });
Why not sort the item before hand, on render() :
render(){
let toDisplay = this.state.items.sort( (a,b) => {
if (a.index <= b.index) {
return -1;
}
if (a.index > b.index) {
return 1;
}
return 0;
});
return(
<div className='foo'>
{
toDisplay.map((item, i) => {
return(
<div className="bar" key={i}>{ item.name }</div>
);
})
}
</div>
);
}
Then you can update the state.items only by using :
this.setState({
items: yourUpdatedItems
});

Categories

Resources