I have REST API endpoint that accepts page, limit (page size) and returns (for simplicity) sorted letters of alphabet (A, B, C, ... Z). Example call of this endpoint is:
let items = await api.items.get({ page: 0, size: 3 })
// items = ["A", "B", "C"]
Now let's say I have implemented infinite scroll and when user scrolls at the of the area, I want to load another 3 items:
const newItems = await api.items.get({ page: 1, size: 3 })
items = [...items, ...newItems]
// items = ["A", "B", "C", "D", "E", "F"]
Everything is fine, however what if user deletes some items, eg. "B" and "D"?
// items = ["A", "C", "E", "F"]
How can I fetch another page of 3 items from correct offset?
const newItems = await api.items.get({ page: 2, size: 3 })
items = [...items, ...newItems]
// items = ["A", "C", "E", "F", "I", "J", "K"] - "G" and "H" has been skipped
Are there any common techniques for this? Unfortunately, I can't modify the API, so I have to use page and size only.
Related
I would like to take an array and split it into a dictionary of sub-arrays based on another array of indices:
const arr = ["A", "B", "C", "D", "E"];
const indices = [0, 0, 1, 0, 3];
// Would like to do something like this:
R.split(indices, array) // { "0": ["A", "B", "D"], "1": ["C"], "3": ["E"] }
Is there an elegant way to do this in Ramda?
Use R.zip combine the arrays to an array of values/indices pairs. Group by the indices, and then take only the values from the pairs:
const { pipe, zip, groupBy, last, map, head } = R
const fn = pipe(
zip, // combine the arrays to an array of value/indice pairs
groupBy(last), // group by the indices
map(map(head)) // take only the values from the pairs
)
const arr = ["A", "B", "C", "D", "E"]
const indices = [0, 0, 1, 0, 3]
const result = fn(arr, indices)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
I have this array of arrays, where the inner arrays are uniformly structured (“tuple-like”) with a unique ID (a string) and a non-unique integer:
[
["A",2],
["B",1],
["C",1]
]
I need the JavaScript function to create the list of permutations below, that does not permute tuples with the same integer value:
[
["A", "B", "C"],
["B", "A", "C"],
["B", "C", "A"]
]
Quoting user pilchard in the comments below, I'm trying to permute “by the second index and avoid duplicates”.
The list should therefore not include these permutations:
[
["A", "C", "B"], // integers same as in ["A", "B", "C"]
["C", "A", "B"], // integers same as in ["B", "A", "C"]
["C", "B", "A"] // integers same as in ["B", "C", "A"]
]
Also, it is preferable to avoid “movement” inside the array. Therefore, ["A", "B", "C"] is a better permutation than ["A", "C", "B"]
In the simplest scenario, the input could be this:
[
["A",1],
["B",1],
["C",1],
["D",1]
]
The result should simply be 1 permutation, as opposed to the 24 that is the result when also permuting identical integers:
[
["A", "B", "C", "D"]
]
Again, “movement” inside the array should be avoided, so ["A", "B", "C", "D"] is the preferred alternative.
Instead of operating on an array of characters, operate on an array of groups of characters. You can take any of these solutions, and instead of removing the chosen character from the input to put it into the output, remove the first character of the chosen group and remove the group only when it's empty.
function groupPermutations(groups) {
if (!groups.length) return [[]];
return groups.flatMap((group, i) => {
// assert(group.length > 0)
const [val, ...rest] = group;
const remaining = rest.length
? [...groups.slice(0,i), rest, ...groups.slice(i+1)]
: [...groups.slice(0,i), ...groups.slice(i+1)];
return groupPermutations(remaining).map(p => [val, ...p]);
});
}
console.log(groupPermutations(["A", "BC"]));
console.log(groupPermutations(["ABCD"]));
Now you only need a trivial conversion of your tuple input format to the grouping:
const pairs = [
["A",2],
["B",1],
["C",1],
];
const byInteger = new Map();
for (const [val, key] of pairs) {
if (!byInteger.has(key)) byInteger.set(key, []);
byInteger.get(key).push(val);
}
const groups = Array.from(byInteger.values());
console.log(groupPermutations(groups));
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 11 months ago.
Improve this question
From this:
[["a", "b"], ["a", "c"], ["a", "d"], ["d", "e"], ["d", "f"], ["f", "g"]]
To this:
[["a", "b"], ["a", "c"], ["a", "d", "e"], ["a", "d", "f", "g"]]
The first element of each inner array represents a "father" and the second one a "son", so the final goal is to find grandsons, grandgrandsons... and then group them in bigger arrays, where all descendants are together.
Maybe it's an easy task, but I've been struggling to figure out how to get it!
If you like to get all relations, without parts which are included in other results, you could get an object of all descendant and start with the parents who are not having parents.
let
getNodes = k => parents[k]
? parents[k].flatMap(getNodes).map(a => [k, ...a])
: [[k]],
data = [["a", "b"], ["a", "c"], ["a", "d"], ["d", "e"], ["d", "f"], ["f", "g"]],
parents = {},
children = new Set,
result;
for (const [p, c] of data) {
(parents[p] ??= []).push(c);
children.add(c);
}
result = Object
.keys(parents)
.filter(p => !children.has(p))
.flatMap(getNodes);
result.map(a => console.log(...a));
How can I remove multiple items by index and save the removed items. I get the currently selected values from a ListBox (e.g.selectedValues = [1, 4, 2]) and have two arrays actives availables. I try to move the selected elements in an efficient way.
That's how I would solve this:
var actives = [ "a", "d", "k", "e"]
var availables = [ "m", "o", "v" ]
var selectedValues = [3, 1]
var elementsToMove = []
selectedValues.forEach(i => {
elementsToMove.push(actives[i])
})
actives = actives.filter(item => !elementsToMove.includes(item))
availables = availables.concat(elementsToMove);
console.log(actives);
console.log(availables);
Expected output:
actives = [ "a", "k" ]
availables = [ "m", "o", "v", "e", "d"]
Note: The length of the arrays can be very large.
A .filter with an .includes inside is O(n ^ 2). For very large inputs, this could be an issue. Consider converting the elementsToMove into a Set instead, turning the overall computational complexity to O(n). You can also construct the elementsToMove array much more concisely by using .map instead of forEach followed by push:
var actives = [ "a", "d", "k", "e"]
var availables = [ "m", "o", "v" ]
var selectedValues = [3, 1];
const elementsToMove = selectedValues.map(i => actives[i]);
const elementsToMoveSet = new Set(elementsToMove);
actives = actives.filter(item => !elementsToMoveSet.has(item))
availables = availables.concat(elementsToMove);
console.log(actives);
console.log(availables);
You could take a single loop and psuh the spliced elements to the other array.
var actives = [ "a", "d", "k", "e"],
availables = [ "m", "o", "v" ],
selectedValues = [3, 1];
selectedValues.forEach(i => availables.push(...actives.splice(i, 1)));
console.log(actives);
console.log(availables);
I need to move a selected item in an array to the start of the array. Lets say I have an array of 5 items
[{"A", "B", "C", "D", "E"}].
Now, say I choose index 2 (which will be C) and I need to move C (index 2) to the start of the Array. Finally the array should display as follows:
[{"C", "A", "B", "D", "E"}].
Solution: You can solve your problem by using nested Array.Splice() methods. I would make this an extension method on arrays.
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
Explanation: The inner splice() is essentially saying take the item you are wanting to move, remove it from the array, and use the value of that item in the outer splice(). The outer splice() is saying to insert the item you just removed from the array at the index you specified in to.
Array.prototype.move is Javascript's way of creating an extension method called move on array objects. Any code that has access to this function can call move(from, to) on any array (e.g. myArray.move(2, 0)). So I would put this somewhere global so all your code can see it!
Example:
var letters = ["A", "B", "C", "D", "E"];
letters.move(2, 0);
// letters is now ["C", "A", "B", "D", "E"]
Here an example, how to do it:
<script>
var people = [
{name: 'Collin', city: 'Omaha', friend: false},
{name: 'Alice', city: 'New York', friend: false},
{name: 'Pasha', city: 'Moscow', friend: true},
{name: 'Denis', city: 'St. Pete', friend: true}
];
function toBeginning(index)
{
var obj = people[index];
people.splice(index, 1);
people.unshift(obj);
}
// To test it
alert(people[0].name); // Alert: Collin
toBeginning(2); // move position 2 (Pasha) to Beginning
alert(people[0].name); // Alert: Pasha