immer - Nested produce calls are not working as expected - javascript

const { produce } = require("immer");
const outer = produce((draft) => {
return inner(draft);
}, {});
const inner = produce((draft) => {
draft.arr = [4, 5, 6];
}, {});
outer().arr.sort();
inner().arr.sort();
link: https://codesandbox.io/s/boring-wiles-ezqtr
There is an error on inner().arr.sort(). (read-only error)
My expectation is that outer().arr.sort() also be an error.
Is there something I'm doing wrong here?

Not sure why you want an nested produce but as my understanding you are trying to write a function that leverage immer to sort an array so, to avoid changing initial array.
This is how you could go and from here use another "produce" function that does that. (Code Sandbox)
const { produce } = require("immer")
const baseArray = [6, 10, 3, 2, 1]
const baseArray2 = [17, 9, 10, 3, 2, 1];
function sortedF(state) {
return produce(state, (draft) => {
const sorted = draft.sort((a, b) => a - b)
console.log(sorted);
});
}
const sorted1 = sortedF(baseArray)
console.log(sorted1); // [1, 2, 3, 6, 10]
This use a carried solution curried-produce
const sortedCarried = produce((draft) => {
const sorted2 = draft.sort((a, b) => a - b)
});
const sorted2 = sortedCarried(baseArray2)
console.log(sorted2); // [1, 2, 3, 9, 10, 17]

Related

How to filter an array and return new array of objects with indexed values?

Given the array const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
How can I filter and return a new array of indexed key/value pair objects for example:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// My fail attempt using filter()
let obj = vals.filter((n, i) => {
return new Object({ i: n % 2 });
});
return obj;
// expected result [{1:2}, {3:4}, {5:6}, {7:8}]
I need to keep the index values as I will filter 2 different arrays with different criteria and associated them later.
Update
Second attempt using map() as suggested in the comments
let obj = vals.map((n, i) => {
if (n % 2) {
return { [i]: n };
}
});
Gives me the following:
[{0:1}, undefined, {2:3}, undefined, {4:5}, undefined, {6:7}, undefined, {8:9}]
To get a list of { key: value } objects where key is the index, and the values are only even without the odd values, you can do this:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = vals.map((v, i) => [i, v])
.filter(([_, v]) => v % 2 == 0)
.map(([i, v]) => ({ [i]: v }));
console.log(result);
With the first map, you make a list of [[0, 1], ...] pairs to save the index for later.
Then you filter your index-value pairs so only even values remain.
Then you pack those pairs into an object in another map.
This can be done more efficiently with a single iteration using reduce:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = vals.reduce((a, v, i) => {
if (v % 2 == 0) {
a.push({ [i]: v });
}
return a;
}, []);
console.log(result);
Youn can try simple for loop or the reduce function
let arr = [];
for(let i = 0; i<vals.length-1;i += 2)
{
let obj={};
obj[vals[i]]=vals[i+1];
arr.push(obj);
};

How to access the data from a promise which is an object property

I have the following array of objects
const people = [
{
id:'John:1:Aries',
exp:40.3,
scores: [4, 4, 5, 6, 4, 6, 5],
futureScores:Promise.resolve([5, 5, 6]),
},
{
id:'Jane:2:Pisces',
exp:57.6,
scores: [6, 5, 6, 6, 4, 5, 6],
futureScores:Promise.resolve([4, 5, 6]),
},
{
id:'Tom:3:Leo',
exp:30.3,
scores: [4, 4, 5, 5, 6, 6],
futureScores:Promise.resolve([4, 4, 6]),
},
{
id:'Amy:4:Sagittarius',
exp:35.0,
scores: [6, 6, 6, 6, 6, 5],
futureScores:Promise.resolve([6, 5, 6]),
},
];
const extractId = (people) => {
return people.filter((person) => {
const id = person.id;
if(id.includes("a")){
return person;
}
})
}
const scores = extractId(people);
const avg = (scores) => {
const getavg = scores.map((el) => el.scores.reduce((a,b) => a +b , 0)/el.scores.length);
const allavg = getavg.reduce((a, b) => a+b/getavg.length, 0)
return allavg;
}
I want to access the futureScores property and record the scores in another variable
Tried the following
const future = (scores) => {
const arr = [];
const fscore = scores.map((el) => {
const fscores = el.futureScores;
const result = fscores.then(function(value){
console.log(value);
arr.concat.value;
});
return result
})
return fscore;
}
console.log(future(scores));
The first console.log is outputting the scores but console.log(future(scores)) is outputting [ Promise { }, Promise { } ]
What am I doing wrong?
It's how you're handling promises within a map. The idea is you should return a map of promises and resolve them. Something like this:
const future = (scores) => {
return scores.map((el) => {
const fscores = el.futureScores;
return fscores.then((value) => value);
})
}
Promise.all(future(scores)).then(res=> {
console.log(res)
})
The idea is to return fscore which is a single promise but since it's a map, at the end we have an array of promises which we can resolve with Promise.all
I did away with arr.concat.value since you're not using it. And if you want to actually change the array, do arr = arr.concat.value; since concat does not mutate the original array, makes a new one.
You need to return the array inside of the map function.
const future = (scores) => {
const arr = [];
const fscore = scores.map((el) => {
const fscores = el.futureScores;
const result = fscores.then(function(value){
console.log(value);
return arr.concat.value;
});
return result
})
return fscore;
}
future(scores).forEach( val => {
console.log(val)
})

rxjs, how to merge several requests, one serial request in parallel requests, and get one result

for example,
I have 2 API service, returning type is Observable.
function add(row) {
let r = Math.ceil(Math.random() * 2000);
let k = row + 1;
return timer(r).pipe(mapTo(k));
}
function mutiple(row) {
let r = Math.ceil(Math.random() * 2000);
let k = row * 10;
return timer(r).pipe(mapTo(k));
}
there is an array [1, 2, 3, 4, 5], I use the two function as follow:
from([1, 2, 3, 4, 5]).pipe(
mergeMap((row) => {
return add(row);
}),
mergeMap((row) => {
return mutiple(row);
}),
toArray()
).subscribe((_) => {
console.log("sub", _);
});
the result is
sub [ 50, 20, 60, 40, 30 ]
the answer is that I want.
however, I don't know the source of the element of the array,
I hope the result is
[ [4, 50], [1, 20], [5, 60], [3, 40], [2, 30] ]
or
[
{ sourceData: 4, result: 50 },
{ sourceData: 1, result: 20 },
...
]
if I use contactMap to keep the sequence, the program will execute one by one,
I don't care the sequence, I just wanna know how to connect the source and the result.
Thank you so much~
Try it in this way?
from([1, 2, 3, 4, 5]).pipe(
mergeMap((row) => {
const index$ = of(row);
return forkJoin([index$, add(row)]);
}),
mergeMap(([index, row]) => {
return forkJoin([of(index), mutiple(row)]);
}),
toArray()
).subscribe((_) => {
console.log("sub", _);
});
or
const handler = v => {
return of(v).pipe(
mergeMap((row) => {
return add(row);
}),
mergeMap((row) => {
return mutiple(row);
}),
)
}
from([1, 2, 3, 4, 5]).pipe(
mergeMap(row => forkJoin([of(row), handler(row)])),
toArray()
).subscribe((_) => {
console.log("sub", _);
});

how to sort an array of numbers in javascript making sure that the first count finished before adding duplicates?

I have this array of numbers that i am working with that currently look this;
count = [1,4,3,1,2,3,4,5,6,2,3,5,7];
How can i transform and sort it to make it look like this;
count = [1,2,3,4,5,6,7,1,2,3,3,4,5];
Please help, any idea out there on how to approach this?
1) Get unique elements and sort
2) Get remaining elements and sort
3) combine (1) and (2) arrays.
count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const spSort = arr => {
const uniq = [...new Set([...arr])];
const rem = [];
const temp_set = new Set([...arr]);
arr.forEach(x => {
if (temp_set.has(x)) {
temp_set.delete(x);
} else {
rem.push(x);
}
});
return [...uniq.sort(), ...rem.sort()];
};
console.log(spSort(count));
Use a Set to create unique numbers and a hash object to keep count of duplicates:
const count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const hash = count.reduce((obj, num) => {
obj[num] = obj[num] ? ++obj[num] : 1;
return obj;
}, {});
const uniq = [...new Set(count)].sort();
uniq.forEach((num, _, arr) => {
while (--hash[num]) arr.push(num);
});
console.info(uniq);

What, why!? Tricky JS code spread ... and [].concat

Was just going through the source code for Laravel Mix (webpack setup) to get some inspiration on setting up my own webpack when I came across this.
rules.push(...[].concat(newRules))
I can't figure out what the point of this is but I trust Taylor wouldn't include anything superfluous just for the sake of it.
Surely any of these are just as good?
rules.concat(newRules)
or
rules.push(...newRules)
or even a good old for-loop! But why concat to empty array before spreading the elements?
Much appreciated if anybody can enlighten me on this.
I can only speculate as I didn't author the code but I imagine the intention is to add newRules to rules where newRules could be any type (not just an array). concat will create a new array while we want the original array mutated. push mutates the array but how do you handle the case where newRules is an array? You can't just push newRules into rules because it'll be an array inside an array and you can't spread newRules because not everything is an iterable. [].concat(newRules) will add all of newRules to an array which essentially 'converts' non-arrays into an array and spreading that array inside push will add those items to rules.
Check out the test cases below and click Run snippet to see it in action:
const PASSED = '✅ PASSED';
const FAILED = '❌ FAILED';
(() => {
console.log('`rules.concat(newRules)`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.concat(newRules);
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.concat(newRules);
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
console.log('');
})();
(() => {
console.log('');
console.log('`rules.push(newRules)`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.push(newRules);
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.push(newRules);
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
console.log('');
})();
(() => {
console.log('');
console.log('`rules.push(...[].concat(newRules))`');
(() => {
const expectation = [1, 2, 3, 4, 5, 6];
const rules = [1, 2, 3];
const newRules = [4, 5, 6];
rules.push(...[].concat(newRules));
console.log('where `newRules` is an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
(() => {
const expectation = [1, 2, 3, 4];
const rules = [1, 2, 3];
const newRules = 4;
rules.push(...[].concat(newRules));
console.log('where `newRules` is not an array:', _.isEqual(expectation, rules) ? PASSED : FAILED);
})();
})();
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>

Categories