Having trouble in reducer to get all api data together - react-native

I am in middle of setting up redux for managing state for all my api data. I have infinite flatlist that grow with query with offset and limit i pass to api param.
Now issue remain — I am able to get first set of data but never combine data of all api calls. I am sure i am doing something silly out there.
I am stuck badly here that investing day and night here. Any help will greatly appreciated here.
Reducer:
import { RECEIVED_NEWS } from './actions';
export const news = (state = [], action) => {
//console.log('action data is '+JSON.stringify(action));
switch (action.type) {
case RECEIVED_NEWS:
return [...state, action.apidata];
default:
return state;
}
};
Action:
export const RECEIVED_NEWS = 'RECEIVED_NEWS';
export const addNews = apidata => ({
type: RECEIVED_NEWS,
apidata
});
Sample api data : https://codebeautify.org/online-json-editor/cb73c978 or https://pastebin.com/rS8Aj4ex
Object dir that i print with console http://navgujaratsamay.co.in/wp-content/uploads/2019/02/Screenshot-2019-02-01-at-5.09.41-PM.png
I am expecting merging all all api calls and i am successfully calling store but every time getting only last call data.

You need to do:
return [...state, ...action.apidata]
because action.apidata is a array too, you need to spread it too otherwise it will get nested. If apidata was not an array just an object, then no need to spread it.
eg:
> let arr1 = [1, 2, 3, 4, 5]
> let arr2 = [6, 7, 8, 9, 0]
> [...arr1, arr2] // wrong
< [1, 2, 3, 4, 5, [6, 7, 8, 9, 0]] // gives a nested array
> [...arr1, ...arr2] // correct
< [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] // merges properly
> let num = 10
> [...arr1, num] // no spreading required when it's not an array
< [1, 2, 3, 4, 5, 10] // merges properly

Related

Continue zip(), if one source is completed

I have some troubles with .zip() operator.
Let me simplify my problem on a small example.
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
List<Flux<Integer>> list1 = Arrays.asList(flux1, flux2, flux3);
TreeSet<Integer> set = new TreeSet<>(Comparator.reverseOrder());
Set<Integer> list = Flux.zip(list1, objects -> {
boolean setChanged = false;
for (Object o : objects) {
Integer i = (Integer) o;
if (set.size() < 5 || i > set.last()) {
setChanged = true;
set.add(i);
if (set.size() > 5) {
set.pollLast();
}
}
}
return setChanged;
}).takeWhile(val -> val)
.then(Mono.just(set))
.block();
System.out.println(list);
Here I have 3 different sources(they are sorted descending by default, and the number of them could be much bigger), and I want to get from them a collection of 5 elements sorted descending. Unfortunately, I can't just use concat() or merge() operators, because sources in a real life can be really big, but I need only small amount of elements.
I am expecting [9, 8, 7, 6, 5] here, but one of the sources is completed after first iteration of zipping.
Could you please suggest how I can get around with this problem?
You can try the reduce operation
#Test
void test() {
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7, 0, -2, 4,3,2,2,1);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
var k = 5;
List<Flux<Integer>> publishers = List.of(flux1, flux2, flux3);
var flux = Flux.merge(publishers)
.log()
.limitRate(2)
.buffer(2)
.reduce((l1, l2) -> {
System.out.println(l1);
System.out.println(l2);
return Stream.concat(
l1.stream(),
l2.stream()
).sorted(Comparator.reverseOrder())
.limit(k)
.collect(Collectors.toList());
})
.log();
StepVerifier.create(flux)
.expectNext(List.of(9,8,7,6,5))
.expectComplete()
.verify();
}
You can fetch data in chunks and compare them to find the top K elements.
In a sequential case it will fetch a new batch, compare it to the current top k result and return a new topk like in the example above (PriorityQueue may work better for sorting if k is big).
If you're using parallel schedulers and batches are fetched in parallel, then it can compare them with each other independently that should be a bit faster.
Also you have full control over the fetched data via rateLimit, buffer, delayElements, etc

How to recover Flux element in case of an error

Consider I have following code:
Flux.fromIterable(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
.flatMap(integer -> {
if (integer == 5) {
throw new RuntimeException("error");
}
return just(Tuples.of(integer, new Random().nextInt()));
})
.onErrorContinue((throwable, o) -> just(Tuples.of(o, 0)))
.log()
.subscribe();
which outputs:
onSubscribe([Fuseable] FluxContextStart.ContextStartSubscriber)
request(unbounded)
onNext([1,-1752848133])
onNext([2,-1719473285])
onNext([3,819220275])
onNext([4,-725013418])
onNext([6,-1693809308])
onNext([7,1457499883])
onNext([8,-740589679])
onNext([9,1718349574])
onNext([10,-861794538])
onNext([11,1016444064])
onComplete()
Is there a way that so I can recover 5 with a default value instead of dropping it?
See onErrorReturn() and onErrorResume(). You probably need to use it inside a flatMap() on the inner Mono over the value to avoid loosing the rest of original Flux values.

How to map a list by chunks in kotlin

I often end up with data sources like (pseudo code below, not any specific syntax, it is just to illustrate):
list = {
"XLabel",
"XDescription",
"YLabel",
"YDescription",
"ZLabel",
"ZDescription"
}
desired output is:
list = {
MyClass("XLabel", "XDescription"),
MyClass("YLabel", "YDescription"),
MyClass("ZLabel", "ZDescription")
}
Is there anything more clean than to do a fold(), and fold it into a new list? I've also rejected doing something weird like list.partition().zip()
I basically want a more powerfull map that would work like mapChunks( it1, it2 -> MyClass(it1, it2)) where the chunking is part of the function so it gets easy and nice. (My example has the list in chunks of two, but 3 is also a prevalent use case.)
Does this function exist? Or what is the most idiomatic way to do this?
You can use the chunked function, and then map over the result. The syntax gets very close to what you wanted if you destructure the lambda-argument:
list.chunked(2)
.map { (it1, it2) -> MyClass(it1, it2) }
// Or use _it_ directly: .map { MyClass(it[0], it[1]) }
I think the windowed method should do what you want.
lst.windowed(size = 2, step = 2, partialWindows = false) { innerList -> MyClass(innerList[0], innerList[1]) }
You can also use chunked but it calls windowed under the hood. But with chunked you can get lists that have fewer elements than you were expecting
EDIT to answer #android developer's question about getting the indexes of the list
val lst = listOf(7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
val windowedList = lst.mapIndexed { index, it -> index to it }
.windowed(size = 2, step = 2, partialWindows = false) {
it[0].first
}
println(windowedList)
Would output
[0, 2, 4, 6, 8]
To add to the existing answers, you can use chunked function with the transform lambda passed as its second argument:
list.chunked(2) { (label, description) -> MyClass(label, description) }
This way is more efficient because the temporary list of two elements is reused across all chunks.
You can create an extension function, for example mapChunks, and reuse it:
fun List<String>.mapChunks(): List<MyClass> {
return chunked(2).map { MyClass(it[0], it[1]) }
}
val list1 = listOf(
"XLabel",
"XDescription",
"YLabel",
"YDescription",
"ZLabel",
"ZDescription"
)
val result1 = list1.mapChunks()
val list2 = listOf(
"XLabel1",
"XDescription1",
"YLabel1",
"YDescription1",
"ZLabel1",
"ZDescription1"
)
val result2 = list2.mapChunks()
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html
Chunked returns a sub list of the size you specify
This is the API call you want
considering your list is in pairs of 2 you can do this
list.chunked(2) //List<List<String>>
.map{MyClass(it[0], it[1]} //list<MyClass>

Vue Object isn't returning updated value despite seeing the value updated

I'm writing tests for Vue.js and I'm trying to write the test to ensure that when some props are changed for pagination, the resulting values within the component are updated properly.
So when I console.log the component, I see the correctly updated values, but then when I try to literally grab that attribute it gives me the old and outdated value. Look at rangeList in the following screenshot to see what I mean:
Here is my code so that you see how what is generating this output.
pagComp.$refs.component.limit = 10;
pagComp.$refs.component.pages = 145;
console.log(pagComp.$refs.component);
console.log('RangList: ' + pagComp.$refs.component.rangeList.length);
Here is the code that modifies rangeList:
createListOfRanges() {
let range = [];
for(let i = 0; i < this.pages; i++) {
range.push(i);
}
this.rangeList = [];
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
this.correctLastRange();
},
Finally, there are two places this function is called: when the component is being created, and when the pages attribute changes. Here is the watcher:
watch: {
pages(val) {
this.createListOfRanges();
}
},
I see some issues with this part of your code:
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
range.splice(..) returns an array, which is getting pushed into this.rangeList
Forget about that for a minute. Look at the following example:
x = [1, 2, 3, 4]
x.splice(0, 2) // result: [1, 2]
As you can see above, splice returns an array, not an element. Now, in the same example above:
x = [1, 2, 3, 4]
y = [10, 11]
y.push(x.splice(0, 2))
Check the value of y. It will be [10, 11, [1, 2] ]. It is an array within another array. It does not look very meaningful here. You can do the above x and y experiments directly in your developer console.
In your case, your x happens to be the local range array within createListOfRanges method, and your y is this.rangeList that belongs to your Vue component.
Can you check your app logic at around that area, and see if that is really what you want to do?
For debugging Vue.js apps, you also need Vue devtools: https://github.com/vuejs/vue-devtools - it is much better than console.log()
While #Mani is right on the line of code giving you issues is your push to rangeList.
createListOfRanges() {
let range = [];
for(let i = 0; i < this.pages; i++) {
range.push(i);
}
this.rangeList = [];
while(range.length > 0) {
this.rangeList.push(range.splice(0, this.rangeLength));
}
this.correctLastRange();
},
pushing the result of the splice just makes a single element with all the elements of range in it.
try changing it to
this.rangeList.push(range.shift());
Though your function could be simplified by pushing the for(let i = 0; i < this.pages; i++) { i value directly to rangeList unless that's a simplification.
This JSFiddle shows what I'm talking about.
I appreciate the answers above, however they aren't what the issue was.
The problem was with Vue's lifecycle. I'm not 100% sure why, but when the page and limit variables are changed it takes another moment for the page watcher (shown above) to get executed and update the component. So thus it wouldn't show up in my tests. So what I did was use nextTick like so, which fixed the problem.
pagVue.limit = 10; // limit and pages change together every time automatically
pagVue.pages = 145;
Vue.nextTick(() => {
expect(pagination.rangeList.length).toBe(25);
})

What is the opposite of _.remove

_.remove($scope.posts, post);
I', using _.remove to remove items from an array using lodash. But how do I add objects again? So what is the opposite of _.remove.
_.remove removes an item from an array, now since you want an opposite of the remove which can be push, there is no _.push available. So, i think it's better to use native push function.
here are few things you can consider:
var posts = [{a:1},{b:3},{f:3}];
var post = {a:1};
_.remove(posts, post); // posts = [{b:3},{f:3}]
to add object at 0 index
posts.unshift(post);//posts = [{a:1},{b:3},{f:3}]
to add object at last index
posts.push(post);//posts = [{b:3},{f:3},{a:1}]
to insert object at an index
posts.splice(1, 0, {g:8}); // posts = [{a:1},{g:8},{b:3},{f:3}]
and you can use _.mixin of course.
_.mixin({
push: function(arr,obj){
return arr.push(obj);
}
});
and you can use it like
_.push(posts,post);
JsFiddle for mixin example
Please try _.fill, fills elements of array with value from start up to
_.fill(array, value, [start=0], [end=array.length])
_.fill([4, 6, 8, 10], '*', 1, 3);
// → [4, '*', '*', 10]