Problem creating Array of References to DOM elements - javascript

I use this hook : react-use-scrollspy that uses an sectionRefs of useRef in array
const sectionRefs = [
useRef(null),
useRef(null),
useRef(null),
];
const activeSection = useScrollSpy({
sectionElementRefs: sectionRefs,
offsetPx: -80,
});
But I use this instead of that array of refs :
const menuItemsRef = {
home: "0",
packs: "4",
faq : "8",
projects: "2",
team : "7",
contact : "6"
}
const menuRef = useRef(Object.keys(menuItemsRef));
let menuRef2 = [];
useEffect(()=>{
menuRef2 = Object.values(menuRef.current).slice(6,12).map((item)=>
{
return { current : item }
});
console.log("menuRef " , menuRef2);
},[]);
const activeSectionNavbar = useScrollSpy({
sectionElementRefs: menuRef2,
offsetPx: -80,
});
Why it didn't work?
To clarify , my problem is to turn this :
const menuItemsRef = {
home: "0",
packs: "4",
faq : "8",
projects: "2",
team : "7",
contact : "6"
}
const menuRef = useRef(Object.keys(menuItemsRef));
in this :
const sectionRefs = [
useRef(null),
useRef(null),
useRef(null),
];

What you are doing creates a ref that contains an array
useRef(Object.keys(menuItemsRef))
// { current: [...] }
What your hook seems to expect is an array that contains refs
const sectionRefs = [
useRef(null),
useRef(null),
useRef(null),
];
// [ { current: ... }, { current: ... }, { current: ... } ]
What you need to do is to keep the "properties" of a react ref (the fact that its content persists across renders) while giving it a structure that matches what your hook is expecting:
const refArray = useRef(Object.values(menuItemsRef).map(value => ({current: value})))
// { current: [ { current: ... }, { current: ... }, { current: ... } ] }
const activeSectionNavbar = useScrollSpy({
sectionElementRefs: refArray.current,
offsetPx: -80,
});

Related

JavaScript modify Array of Objects and alter contained data

I am having difficulties formatting some data. Currently, I receive data in the following structure.
[
{
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
}
]
I essentially need to modify this or even create a new object, that takes the following structure.
[
{
id: 1, //q1
answers: [
{
answer: '5',
},
],
},
{
id: 2, //q2
answers: [
{
answer: '13',
},
{
answer: '12',
},
],
},
{
id: 3, //q3
answers: [
{
answer: 'test',
},
],
},
];
So the id in the above would be obtained by remove the q and getting the number in the first data object. It would then have an answers array that would have an object for each answer.
I have been attempting this but have gotten lost. I don't know if I should use loops, mapping, filters etc. To be honest, the furthest I have got so far is obtaining the keys
var modified = data.map(function(item) {
return Object.keys(item)
})
I have created a JSFiddle where I have been attempting to do this.
Is there any way I can achieve the data I am after?
Many thanks
Please use map function.
const data = {
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
};
const result = Object.keys(data).map(key => {
let item = {id: key.substring(1), answers: []};
if(typeof data[key] === "string")
item.answers.push({answer: data[key]});
else
item.answers = data[key].map(val => ({answer: val}));
return item;
});
console.log(result)
const inputData = [
{
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
}
]
function answerMapper(objVal, id){
return Array.isArray(objVal)
?
{ id, answers: objVal.map(answer => ({ answer }))}
:
{ id, answers: [{answer: objVal }] }
}
function formatObject(obj){
return Object.keys(obj).map((k, i) => answerMapper(obj[k], i+1));
}
const result = inputData.map(obj => formatObject(obj));
// remove flatMap if your inputData has more than one entry
console.log(result.flatMap(x => x));
map over the first element of the data with Object.entries, grab the key and value, create a new answers array and return a new object.
const data = [{
"q1": "5",
"q2": [
"13",
"12",
],
"q3": "test",
}];
const out = Object.entries(data[0]).map(obj => {
const [ key, value ] = obj;
const id = Number(key[1]);
// If the the value is an array
// return a new array of mapped data
// Otherwise return an array containing
// one object
const answers = Array.isArray(value)
? value.map(el => ({ answer: el }))
: [{ answer: value }];
// Return the new object
return { id, answers };
});
console.log(out);
lets create a pure function which accepts the object in the array like so
const processObject = obj => Object.keys(obj).map(id => {
const answer = obj[id];
const answers = Array.isArray(answer) ? answer : [answer]
const answerObjectArray = answers.map(ans => ({
answer: ans
}));
return {
id: +id.substring(1),
answers: answerObjectArray
}
});
const dataArray = [{
"q1": "5",
"q2": [
"13",
"12",
],
"q3": "test",
}];
const output = processObject(dataArray[0]);
console.log(output);

How to change an object in an array by finding the id's similar to id's in another array in JavaScript in expo/react-native?

I've tried doing this by using an array methods and also looping, but It didn't work, it only somehow changes the first Id's object,
const [data,setData] = useState([
{
id:"1",
isSelected:true,
},
{
id:"2",
isSelected:false,
},
{
id:"3",
isSelected:false,
},
]);
const tasks = ["1","2"]
const selectPackage = async(tasks)=>{
if(tasks){
tasks.filter((tval,ti)=>{
let newDataArray = data.map((data,i)=>{
if(tval == data._id){
return {...data,isSelected:true}
}else{
return data;
}
});
setData(newDataArray);
setPackageView(false);
})
}
}
Basically what I am trying to do is:
Suppose you have an object like:
const data = [
{
id:"1",
isSelected:true,
},
{
id:"2",
isSelected:false,
},
{
id:"3",
isSelected:false,
},
];
And you're given some ID's of some objects in another array like:
const IDs = ["1","3"];
all I wanna do is change the value of isSelected of the corresponding id to be true.
Please help, thank you so much! :)
const data = [{
id: "1",
isSelected: true,
},
{
id: "2",
isSelected: false,
},
{
id: "3",
isSelected: false,
},
];
const IDs = ["1", "3"];
data.map(item => {
if (IDs.includes(item.id)) {
item.isSelected = true;
}
return item;
});

Convert paths with items to tree object

I'm trying to convert an array of object contains paths with item to tree of data so I wrote a function path loop on the path:
From this array:
[
{ userName: "1", tags: ["A;B"] },
{ userName: "2", tags: ["A;B"] },
{ userName: "3", tags: ["A;"] },
{ userName: "4", tags: ["A;B;C"] },
{ userName: "5", tags: ["A;B"] },
{ userName: "6", tags: ["A;B;C;D"] }
]
to this structure:
[{
name: "A",
families: [{
name: "B",
families: [{
name: "C",
families: [{
name: "D",
families: [],
items: ["6"]
}],
items: ["4"]
}],
items: ["1", "2", "5"]
}],
items: ["3"]
}]
function convertListToTree(associationList) {
let tree = [];
for (let i = 0; i < associationList.length; i++) {
let path = associationList[i].tags[0].split(';');
let assetName = associationList[i].userName;
let currentLevel = tree;
for (let j = 0; j < path.length; j++) {
let familyName = path[j];
let existingPath = findWhere(currentLevel, 'name', familyName);
if (existingPath) {
if (j === path.length - 1) {
existingPath.items.push(assetName);
}
currentLevel = existingPath.families;
} else {
let assets = [];
if (j === path.length - 1) {
assets.push(assetName)
}
let newPart = {
name: familyName,
families: [],
items: assets,
};
currentLevel.push(newPart);
currentLevel = newPart.families;
}
}
}
return tree;
}
function findWhere(array, key, value) {
let t = 0;
while (t < array.length && array[t][key] !== value) {
t++;
}
if (t < array.length) {
return array[t]
} else {
return false;
}
}
But I have some issue here that the expected output is not like I want
[
{
"name": "A",
"families": [
{
"name": "B",
"families": [
{
"name": "C",
"families": [
{
"name": "D",
"families": [],
"items": [
"6"
]
}
],
"items": [
"4"
]
}
],
"items": [
"1",
"2",
"5"
]
},
{
"name": "",
"families": [],
"items": [
"3"
]
}
],
"items": []
}
]
Can someone please help me to fix that
You should be able to use recursion to achieve this, using getFamilies and getUsers functions called at each level:
const allTags = ["A", "B", "C", "D"];
let a = [ { "userName": "1", "tags": ["A;B"] }, { "userName": "2", "tags": ["A;B"] }, { "userName": "3", "tags": ["A;"] }, { "userName": "4", "tags": ["A;B;C"] }, { "userName": "5", "tags": ["A;B"] }, { "userName": "6", "tags": ["A;B;C;D"] } ];
// This function assumes order is not important, if it is, remove the sort() calls.
function arraysEqual(a1, a2) {
return a1.length === a2.length && a1.sort().every(function(value, index) { return value === a2.sort()[index]});
}
function getUserNames(tags, arr) {
return arr.filter(v => arraysEqual(v.tags[0].split(';').filter(a => a),tags)).map(({userName}) => userName);
}
function getFamilies(tags) {
if (tags.length >= allTags.length) return [];
const name = allTags[tags.length];
const path = [...tags, name];
return [{ name, families: getFamilies(path), items: getUserNames(path, a)}];
}
let res = getFamilies([]);
console.log('Result:', JSON.stringify(res, null, 4));
The idea here is to iterate the data (the reduce loop), and whenever a node is missing from the Map (nodesMap), use createBranch to recursively create the node, create the parent (if needed...), and then assign the node to the parent, and so on. The last step is to get a unique list of root paths (A in your data), and extract them from the Map (tree) to an array.
const createBranch = ([name, ...tagsList], nodesMap, node) => {
if(!nodesMap.has(name)) { // create node if not in the Map
const node = { name, families: [], items: [] };
nodesMap.set(name, node);
// if not root of branch create the parent...
if(tagsList.length) createBranch(tagsList, nodesMap, node);
};
// if a parent assign the child to the parent's families
if(node) nodesMap.get(name).families.push(node);
};
const createTree = data => {
const tree = data.reduce((nodesMap, { userName: item, tags: [tags] }) => {
const tagsList = tags.match(/[^;]+/g).reverse(); // get all nodes in branch and reverse
const name = tagsList[0]; // get the leaf
if(!nodesMap.has(name)) createBranch(tagsList, nodesMap); // if the leaf doesn't exist create the entire branch
nodesMap.get(name).items.push(item); // assign the item to the leaf's items
return nodesMap;
}, new Map());
// get a list of uniqnue roots
const roots = [...new Set(data.map(({ tags: [tags] }) => tags.split(';')[0]))];
return roots.map(root => tree.get(root)); // get an array of root nodes
}
const data = [{"userName":"1","tags":["A;B"]},{"userName":"2","tags":["A;B"]},{"userName":"3","tags":["A;"]},{"userName":"4","tags":["A;B;C"]},{"userName":"5","tags":["A;B"]},{"userName":"6","tags":["A;B;C;D"]}];
const result = createTree(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Allow me to make two small changes, and ramda's mergeDeepWithKey will do most of the work for you.
Changes, before we start:
Make tags an array rather than an array containing one string (i.e. tags[0].split(";"))
Allow families to be a dictionary-like object rather than an array (if you ever need your array format, it's Object.values(dict))
Solution:
Transform every entry to a path of the desired format using reduce
Merge all paths with custom logic:
When merging name entries, don't change the name
When merging items entries, concatenate
const inp = [
{ userName: "1", tags: ["A","B"] },
{ userName: "2", tags: ["A","B"] },
{ userName: "3", tags: ["A"] },
{ userName: "4", tags: ["A","B","C"] },
{ userName: "5", tags: ["A","B"] },
{ userName: "6", tags: ["A","B","C","D"] }
];
// Transform an input element to a nested path of the right format
const Path = ({ userName, tags }) => tags
.slice(0, -1)
.reduceRight(
(families, name) => ({ name, families: { [families.name]: families },
items: []
}),
({ name: last(tags), families: {}, items: [userName] })
);
// When merging path entries, use this custom logic
const mergePathEntry = (k, v1, v2) =>
k === "name" ? v1 :
k === "items" ? v1.concat(v2) :
null;
const result = inp
.map(Path)
// Watch out for inp.length < 2
.reduce(
mergeDeepWithKey(mergePathEntry)
)
console.log(JSON.stringify(result, null, 2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const { mergeDeepWithKey, last } = R;</script>

How to delete nested state object in immutable-js inside reducer?

I want to delete an entity from data (which is List inside object someData). I am using fromJS of immutable js in my reducer to keep the state immutable
I tried using updateIn, deleteIn, update, removeIn and whatever I could find on immutable-js. But it didn't work for me. Most probably I am using these functions the wrong way.
import { fromJS, updateIn } from 'immutable';
import * as type from './constants';
export const initialState = fromJS({
someData: [],
loading: true,
});
function someReducer(state = initialState, action) {
switch (action.type) {
case type.DELETE_SINGLE_ENTITY:
updateIn(state, ['someData', 'data'], val =>
val.filter(x => x.id !== action.id),
);
return state;
default:
return state;
}
}
export default someReducer;
//example someData
/*
{
date: "",
data: [
{
"id": "1",
"machine_type": "xyz",
"created_time": "2019-06-18T10:36:60Z",
...
},
{
"id": "22",
"machine_type": "abc",
"created_time": "2019-06-20T10:36:60Z",
...
},
{
"id": "2",
"machine_type": "kjhkh",
"created_time": "2019-06-11T12:36:60Z",
...
}
]
}
*/
I want to delete an entity matching with the id passed in action.
Before deleting the output of state.get('someData') is in the above example. My expected output (when action.id is 2) when I type state.get should be:
{
date: "",
data: [
{
"id": "1",
"machine_type": "xyz",
"created_time": "2019-06-18T10:36:60Z",
...
},
{
"id": "22",
"machine_type": "abc",
"created_time": "2019-06-20T10:36:60Z",
...
}
]
}
Finally! Got it!
this:
return updateIn(state, ['someData', 'data'], val =>
val.filter(x => x.id !== action.id),
);
instead of this:
updateIn(state, ['someData', 'data'], val =>
val.filter(x => x.id !== action.id),
);
return state;
Previously I thought updateIn would update the state itself but it doesn't it returns the updated object. So just returning the updateIn would be fine.
You can do it using the filter function
const sampleData = [{id: 1},{id: 2},{id: 3}]
const idToRemove = 2;
const updatedData = sampleData.filter(el => {
return el.id !== idToRemove
})
console.log(updatedData);

Get all children from parent JSON data in React

This is an example of my json data
[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"52",
"Padre":"Telefonia",
"Hijo":"Configuracion"
},
{
"Id":"70",
"Padre":"Telefonia",
"Hijo":"Rutas"
}
]
So far I have achieved the following data in console.log:
(3) [{…}, {…}, {…}]
0: {Padre: "CRM", Hijo: "Argumentarios", Description: "SALUD NORMAL", Id: "114"}
1: {Padre: "Permisos", Hijo: "root", Description: "Usuarios", Id: "44"}
2: {Padre: "Telefonia", Hijo: "Audio", Description: "Locuciones", Id: "50"}
I need to show all the children of each parent element.
I am creating a menu and I want the submenu associated with each parent to appear. I would like the children not to appear repeated. In my json parent it's Padre and Child is Hijo (is in spanish).
This is my original code:
componentWillMount(){
fetch('fake-son.php')
.then(response => response.json())
.then(menuSubmenu =>{
const result = [];
const map = new Map();
for (const item of menuSubmenu) {
if(!map.has(item.Padre)){
map.set(item.Padre, true); // set any value to Map
result.push({
Padre: item.Padre,
Hijo: item.Hijo,
Description: item.Description,
Id:item.Id
});
}
}
this.setState({
menuSubmenu:this.state.menuSubmenu.concat(result)
})
console.log(result);
})
}
Can you help me show all the children about their father? Thanks a lot
You can use array.reduce to create a relation like so,
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
parentList: []
};
}
componentDidMount() {
//call your rest api here...
const list = [{
"Id": "114",
"Padre": "CRM",
"Hijo": "Argumentarios"
},
{
"Id": "115",
"Padre": "CRM",
"Hijo": "Argumentarios"
},
{
"Id": "44",
"Padre": "Permisos",
"Hijo": "root"
},
{
"Id": "45",
"Padre": "Permisos",
"Hijo": "root"
},
{
"Id": "50",
"Padre": "Telefonia",
"Hijo": "Audio"
},
{
"Id": "52",
"Padre": "Telefonia",
"Hijo": "Configuracion"
},
{
"Id": "70",
"Padre": "Telefonia",
"Hijo": "Rutas"
}
];
const PadreMap = list.reduce((acc, obj) => {
if (!acc[obj.Padre]) {
acc[obj.Padre] = {
...obj,
Hijo: [obj.Hijo]
};
} else {
!acc[obj.Padre].Hijo.includes(obj.Hijo) && acc[obj.Padre].Hijo.push(obj.Hijo)
}
return acc;
}, {});
this.setState({parentList: Object.keys(PadreMap).map((padre) => ({
name: padre,
children: PadreMap[padre].Hijo
}))})
}
render() {
return <div >{
this.state.parentList.map(parent => <ul>{parent.name}:
{parent.children.map(hijo => <li>{hijo}</li>)}
</ul>)
}< /div>
}
}
ReactDOM.render( < Demo / > , document.getElementById('app'));
li{
margin-left: 30px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Yes you can show it by the command object.toSource() it show all the object attribute...
Use the following chunk in place of your map code
menuSubMenu.forEach((item)=>{
if(item){
const key = item.Padre;
if(!map.get(key)){
map.set(key,[item]);
}else{
map.get(key).push(item); // if you need just child name then push item.Hijo
}
} });
This code will give you a unique parent map as shown below
For iterating it as array and concat the results
let results = Array.from(map);// in results [0] index will have name of parent [1] index will have the array of values
You can do that simply like:
let data=[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"52",
"Padre":"Telefonia",
"Hijo":"Configuracion"
},
{
"Id":"70",
"Padre":"Telefonia",
"Hijo":"Rutas"
}
]
const mapChildren=()=>{
const newData=data.reduce(function (r, a) {
a=Object.assign({},a)
r[a.Padre] = r[a.Padre] || [];
r[a.Padre].push(a);
delete a.Padre;
return r;
}, Object.create(null));
return newData;
}
console.log('#Children:',mapChildren(),typeof(mapChildren()))
console.log('#PArents:',Object.keys(mapChildren()))

Categories

Resources