So I'm trying to change the order of an array using redux and immutable. What I'm basically doing is grabbing the current array, modifying it, and updating the state with modified copy, like so:
case MOVE_LAYOUT: {
const index = action.payload;
const lower = index - 1;
const element = state.getIn(['someArray', index]);
if (action.direction === 'up') {
const arr = state.get('someArray');
const newArr = arr.splice(lower, 0, element);
const remove = newArr.splice(index, 1);
return state.set('someArray', remove);
}
return false;
}
For the sake of the question, let's assume action.payload is passed properly, and that a console.log(remove.toJS()) will return what I want. However, when I update someArray nothing happens. Why would this be?
So here's the answer:
case MOVE_LAYOUT: {
const index = action.payload;
const arr = state.get('someArray').toJS();
let next;
if (action.direction === 'up') {
next = index - 1;
} else {
next = index + 1;
}
const temp = arr[index];
arr[index] = arr[next];
arr[next] = temp;
return state.set('someArray', fromJS(arr));
}
Basically, I'll make a copy of the array, and modify the index (move by 1 up or 1 down), and set the modified array copy back to state. Problem with initial approach was not using .toJS() and .fromJS(), so the results were immutable list, rather than JS object.
Related
For some reason, when watching for Pinia state changes, the newVal and oldVal in the watch function are always identical (except for the very first time).
This is how my code looks like:
const { searchFilters } = storeToRefs(searchFiltersStore)
watch(searchFilters.value.categories, (newVal, oldVal) => {
if (
newVal.length !== oldVal.length ||
newVal.every((value, index) => value !== oldVal[index])
) {
reset(undefined, true)
}
}, { deep: true })
searchFilters.value.categories are string[] (array made of strings) type and initially is an empty array.
When the first change is triggered, the oldVal is an empty array and the newVal is whatever it supposed to be.
But after that, every new trigger has oldVal the same as newVal therefore, my if statement is never valid.
What am I missing?
I found that in order to watch an array, you have to pass a copy of the array and not the existing array like so:
watch(() => [...searchFilters.value.categories], (newVal, oldVal) => {
...
});
Also, the deep parameter in my case was not necessary.
Hi all trying to write some logic in paper.js (also using opentype.js for font data) so that when a number contains two or more consecutive zeros' the zero path is divided so that it is solid.
Things i know a zero path, using my particular font, is made up of an outer path with 19 segments and an inner path made up of 18 segments
So I thought would try to iterate over all paths check if a path has 19 segments and the next path has 18 segments and call path.unite() which kind of works. But I only want it to do this with consecutive '0' eg '100', '1000' but not 10.
So i was trying to do an if else statment where if-else (the current path has 18 segments and the next path is less than 18 segments) if true then do nothin or call path.divide()?
Im sure there is a way better way of doing this. Can you help please.
link to codepen
paper.install(window);
window.onload = () => {
paper.setup("canvas");
opentype.load(
"https://assets.codepen.io/1070/pphatton-ultralight-webfont.woff",
(err, font) => {
if (err) {
console.log(err);
} else {
const fontPath = font.getPath("10k", 0, 100, 100).toSVG();
const count = new paper.CompoundPath(fontPath);
count.unite();
count.children.forEach((child, i) => {
if (
child.segments.length === 19 &&
count.children[i + 1].segments.length === 18
) {
const eye = child.unite();
eye.selected = true;
} else if(
count.children[i + 1].segments.length === 18
&& child.segments.length < 18
) {
console.log('hello');
// const target = child.divide();
count.children[i].fillColor = 'black'
} else{
}
});
// const flatCount = count.children[1].unite()
// console.log(count.children[2].segments.length)
// const flatCountTwo = count.children[5].unite()
// flatCount.translate(5,0)
count.fillColor = "red";
project.activeLayer.fitBounds(view.bounds.scale(0.6));
}
}
);
};
I think that rather than using Font.getPath to retrieve a single svg path for the whole text, you should use Font.getPaths to retrieve an svg path for each character.
This way you can quite simply do your analysis on the input string directly and handle the consecutive 0 differently than other characters.
Edit
In order to detect the consecutive zeros, yes, you could use a regex or loop over the characters, like I did in the following example.
Here's a fiddle showcasing a possible solution.
const handleZero = (path) => {
path.children = path.children.slice(0, 1);
};
const getConsecutiveZerosIndices = (content) => {
const zero = '0';
return [...content]
.map((char, i) => ({ char, i }))
.filter(({ char, i }) => {
const previousCharacter = content?.[i - 1];
const nextCharacter = content?.[i + 1];
return char === zero && (previousCharacter === zero || nextCharacter === zero);
})
.map(({ i }) => i);
};
const drawText = (content, font) => {
const fontPaths = font.getPaths(content, 0, 100, 100);
const consecutiveZerosIndices = getConsecutiveZerosIndices(content);
const paths = fontPaths.map((fontPath, i) => {
const path = new paper.CompoundPath(fontPath.toSVG());
if (consecutiveZerosIndices.includes(i)) {
handleZero(path);
}
return path;
});
const group = new paper.Group(paths);
group.fillColor = 'red';
return group;
};
const draw = (font) => {
const path1 = drawText('10k', font);
const path2 = drawText('100k', font);
const path3 = drawText('1000k', font);
path2.position = path1.position.add(0, path1.bounds.height * 1.2);
path3.position = path2.position.add(0, path2.bounds.height * 1.2);
paper.project.activeLayer.fitBounds(paper.view.bounds.scale(0.6));
};
paper.setup('canvas');
opentype.load(
'https://assets.codepen.io/1070/pphatton-ultralight-webfont.woff',
(err, font) => draw(font)
);
I am scraping a table and each row has a button to show a modal with information. I need to scraping the information from the modal for each row but I dont know how to open the modal. I have tried with page.click(selector) inside page.evaluate() but It didnt work.
I have tested the next code.
const users = await page.evaluate(() => {
const usersTable = Array.from(document.querySelectorAll('table#grilla > tbody > tr'))
const userInfo = usersTable.map(async userTable => {
await page.click('td > a#modificar-2')
await page.click('div > button[type=submit].close')
const username = userTable.children[2].innerText
const firstName = userTable.children[4].innerText
const lastName = userTable.children[5].innerText
const email = userTable.children[6].innerText
const estado = userTable.children[7].innerText
const fullName = firstName + lastName
return { username, fullName, email, estado }
})
return userInfo
})
I dont know how to pass page.click() or another option inside page.evaluate()
If you use page.evaluate() you're switching the context from node puppeteer to browser, so you have to use JS native functions like
click: document.querySelector(selector).click().
If you have errors like Error: Evaluation failed: TypeError: Cannot read property 'click' of null probably the element you wanted to click isn't on the page (or it's hidden or something).
From inside that loop you would do:
userTable.querySelector('td > a#modificar-2').click()
(no await)
You can return css selectors of elements you want to click from page.evaluate and then perform page.click on them. For getting css selector for the element you can use the code from an answer to this question:
const elemToClick = page.evaluate(() => {
let elem;
//...
return cssPath(elem);
function cssPath(el) {
if (!(el instanceof Element)) return;
var path = [];
while (el.nodeType === Node.ELEMENT_NODE) {
var selector = el.nodeName.toLowerCase();
if (el.id) {
selector += '#' + el.id;
path.unshift(selector);
break;
} else {
var sib = el,
nth = 1;
while ((sib = sib.previousElementSibling)) {
if (sib.nodeName.toLowerCase() == selector) nth++;
}
if (nth != 1) selector += ':nth-of-type(' + nth + ')';
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
}
});
page.click(elemToClick);
I'm trying to add an object to /users/[userKey]/invitedTo but set deletes the existing data, so does update.
What I want to end up with is something like this:
users
-uniqueuserkey
--name: Name
--InvitedTo
---eventuniquekey1
----eventname: event name 1
----etc
---eventuniquekey2
----eventname: event name 2
----etc
-
// this.event.push(eventObj);
this.event.push(eventObj).then((item) => {
if (item) {
const itemKey = item.key;
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
const invitedTo = this.db.object(`/users/${key}/invitedTo`);
invitedObj[itemKey] = eventObj;
invitedTo.set( { invitedObj } );
}
}
}
});
Update does exactly what I need, but it also deletes existing value:
for (const key in guests) {
if (guests.hasOwnProperty(key)) {
const invitedObj = {};
invitedObj[itemKey] = eventObj;
this.users.update(key, { invitedTo: invitedObj });
}
}
Should I just get the existing data and add to it?
If you want to add an object, you (usually) should use the push method instead of set or update:
const invitedTo = this.db.list(`/users/${key}/invitedTo`);
invitedTo.push(eventObj);
That way, Firebase will create a unique key and add it to the invitedTo node.
If you want to set the key yourself, then you could use the update method like this:
const invitedTo = this.db.object(`/users/${key}/invitedTo/${itemKey}`);
invitedTo.update(eventObj);
How to set a property to value that should be resolve.. like this one..
const getDataFromServer = (id) => ({id: * 2})
R.set(payloadProp, getDataFromServer)({id: 4}); // WRONG, is setting to a function instend to resolve a function with `{id: 4}`
const fetch = (id) => {
return { num: id * 2 }
};
const idProp = R.lensProp('id');
const getDataFromServer = R.pipe(R.view(idProp), fetch);
const payloadProp = R.lensProp('payload');
const setPayloadFromFetch = R.set(payloadProp, getDataFromServer); // NOT WORK, return payload as function
const obj = { id: 1, payload: { message: 'request' } }
const ret = setPayloadFromFetch(obj);
console.log(ret);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.23.0/ramda.min.js"></script>
The problem is that R.set takes a value, not a function, for its second parameter. And you can't switch to R.over, which does take function but calls it with the current value at that lens, not the full data object supplied to the outer function.
The simplest way to solve this is simply to pass the object in both places:
const setPayloadFromFetch = obj => R.set(payloadProp, getDataFromServer(obj), obj);
But if you're intent on making this point-free, lift is your friend:
const setPayloadFromFetch = R.lift(R.set(payloadProp))(getDataFromServer, identity);
although you could also use R.ap, which would be very nice except that R.set takes its parameters in the wrong order for it, and so you have to use R.flip
const setPayloadFromFetch = R.ap(R.flip(R.set(payloadProp)), getDataFromServer);
You can see all these in the Ramda REPL.