how to iterate through two arrays simultaneously with lodash - lodash

I have
A = [1,2,3,4,5,6,7]
B = [3,5,9]
I want to get an array C containing the last elements < than elements in B, like
C = [2,4,7], by using Lodash
I tried
C=_.map(A, a = (v) -> v == _.findLast(A, b = (v) -> v < _.forEach(B, c = (v) -> v==v)))
which does not work. Above I used coffeescript instead of javascript, but please reply with either, I want a solution using lodash, without explicit looping through B elements, thank you.

The forEach is redundant, and just returns the original collection:
const A = [1,2,3,4,5,6,7]
const B = [3,5,9]
// C = [2,4,7], by using Lodash
const result = _.map(B, n => _.findLast(A, m => m < n));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

I know you said you want a lodash soultion, but just in case you were curious here is a way to get your desired array using vanilla ES6:
const A = [1, 2, 3, 4, 5, 6, 7];
const B = [3, 5, 9];
const C = findEls(A, B);
function findEls(A, B) {
const copy = [...A].reverse(); //Copy since reverse is an in-place operation
return B.map(b => {
return copy.find(a => a < b);
});
}
console.log(C);

Related

Multiple props in Ramda lens

Is there a way to apply transforms to multiple keys of an object in Ramda? I am aware this is achievable by R.evolve, but I am interested in knowing if this can be achieved by some modification of lenses.
E.g.:
const data = {
a: "100",
b: "non_numeric_string",
c: "0.5"
}
const numerize = x => +x
const mapping = {
a: numerize,
c: numerize
}
magicFunction(mapping, data)
output:
{
a: 100,
b: "non_numeric_string",
c: 0.5
}
The whole point of a lens is to focus on one part of a data structure. While it is not hard to write something using lensProp to achieve this, I'm don't think it's either very satisfying or a particularly appropriate use of lenses. Here's one Ramda solution:
const magicFunction = (mapping, data) =>
reduce
( (o, [k, fn]) => over (lensProp(k), fn, o)
, data
, toPairs (mapping)
)
const numerize = x => Number (x)
const mapping = {
a: numerize,
c: numerize
}
const data = {a: "100", b: "non_numeric_string", c: "0.5"}
console .log (
magicFunction (mapping, data)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script> const { lensProp, over, reduce, toPairs } = R </script>
But note that a plain ES6 function does the job just as simply, without using lenses:
const magicFunction = (mapping, data) =>
Object.entries (mapping). reduce
( (o, [k, fn]) => ({...o, [k]: fn (o [k]) })
, data
)
Lenses simply don't gain you much here.

custom sum elements by key using lodash

I do have two objects containing keys like
var a = {bar:[1,2], foo:[7,9]}
var b = {bar:[2,2], foo:[3,1]}
I want to get the fallowing results:
var c = {bar:[3,4], foo:[10,10]}
I already have a for logic like:
for (let key in b) {
if (a[key]) {
a[key][0] += b[key][0];
a[key][1] += b[key][1];
}
else a[key] = b[key];
}
But I would like to make this logic in a lodash way. How can I Do it?
You can use create a function that takes n objects, and collects them to an array using rest parameters. Now you can spread the array into _.mergeWith() to combine the objects, and in the customizer function sum the items in the arrays using Array.map() or lodash's _.map() and _.add():
const { mergeWith, isArray, map, add } = _
const fn = (...rest) => _.mergeWith({}, ...rest, (o = [], s) =>
map(s, (n, i) => add(n, o[i]))
)
const a = {bar:[1,2], foo:[7,9]}
const b = {bar:[2,2], foo:[3,1]}
const c = {bar:[3,2], foo:[5,6]}
const d = {bar:[4,2], foo:[5,4]}
const result = fn(a, b, c, d)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You can also use lodash/fp to create a function that merges all values to a multidimensional array with _.mergeAllWith(), then transpose the arrays using _.zipAll(), and sums each array:
const { rest, flow, mergeAllWith, isArray, head, mapValues, zipAll, map, sum } = _
const fn = rest(flow(
mergeAllWith((o, s) => [...isArray(head(o)) ? o : [o], s]), // combine to a multidimensional array
mapValues(flow(
zipAll,
map(sum)
)),
))
const a = {bar:[1,2], foo:[7,9]}
const b = {bar:[2,2], foo:[3,1]}
const c = {bar:[3,2], foo:[5,6]}
const d = {bar:[4,2], foo:[5,4]}
const result = fn(a, b, c, d)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
You can accomplish this using plain JavaScript with Object.entries, concat and reduce:
const a = { bar: [1,2], foo: [7,9] };
const b = { bar: [2,2], foo: [3,1] };
const entries = Object.entries(a).concat(Object.entries(b));
const result = entries.reduce((accum, [key, val]) => {
accum[key] = accum[key] ? accum[key].map((x, i) => x + val[i]) : val;
return accum;
}, { });
console.log(result);

I am trying to implement lodash _.difference(array, [values]) using .filter() method

The method/function needs to return an array that has elements in the 1st array that are not present in the second array.
like,
var arr1 = [1,2,3];
var arr2 = [2,3,4,5,6];
should return [1];
and I need to do this using .filter() method!
You can use Array.filter() with Array.includes():
const arr1 = [1,2,3];
const arr2 = [2,3,4,5,6];
const difference = (a, b) => a.filter(item => !b.includes(item));
const result = difference(arr1, arr2);
console.log(result);

"Count where" in a collection

Using lodash, what would be a good way to count the number of objects in a collection conditionally? Say I wanted to count the number of objects where
a < 4
in the following collection
[{a : 1}, {a : 2}, {a : 3}, {a : 4}, {a : 5}, {a : 6}]
Solution
You can use countBy:
const total = _.countBy(
array,
({ a }) => a < 4 ? 'lessThanFour' : 'greaterThanFour'
).lessThanFour
Alternative
Using sumBy:
const total = _.sumBy(
array,
({ a }) => Number(a < 4)
);
And here's the same but with lodash/fp:
const count = _.sumBy(_.flow(_.get('a'), _.lt(4), Number), objects);
Below you can find an easy way to achieve that using the filter method:
var b = _.filter(a, function(o) { if (o.a < 4) return o }).length;
You can use _.countBy:
const count = _.countBy(arr, o => o.a < 4).true

Using Linq to group, order and concatenate strings

Suppose I have the following data
Key ID Data
A 1 Hello
A 2 World
B 2 Bar
B 1 Foo
I am looking to produce the result
A HelloWorld
B FooBar
I am struggling to get the syntax quite right - I was trying to use Aggregate, but I wasn't sure if I could (or should) use SelectMany
I'd be grateful of any help.
Dim data = result.Rows.
GroupBy(Function(r) r.Key).
Select(Function(g) g.OrderBy(Function(s) s.ID)).
Aggregate(New StringBuilder, Function(cur, nxt)
cur.Append(nxt.First.Data))
Thanks
Simon
I think this (C#) should work:
var data = from r in result.Rows
group r by r.Item("Key").ToString() into g
select new {
g.Key,
Joined = string.Join("", g.OrderBy(s => s.Item("ID"))
.Select(s => s.Item("Data")))
};
Dim result = result.Rows.GroupBy(Function(r) r.Key).Select(Function(g) New With { _
g.Key, _
String.Join("", g.OrderBy(Function(r) r.ID)) _
})
Here's an alternative implementation:
var source = new Item[]
{
new Item { Key = "A", ID = 1, Data = "Hello" },
new Item { Key = "A", ID = 2, Data = "World" },
new Item { Key = "B", ID = 2, Data = "Bar" },
new Item { Key = "B", ID = 1, Data = "Foo" }
};
var results = source
.GroupBy(item => item.Key)
.Select(group => group
.OrderBy(item => item.ID)
.Aggregate(new Item(), (result, item) =>
{
result.Key = item.Key;
result.Data += item.Data;
return result;
}));
You don't want to Aggregate the groups. You want to aggregage the elements of each group unto itself.
If you want the query to do it, then
Dim data = result.Rows
.GroupBy( )
.Select(Function(g) g
.OrderBy( )
.Aggregate( )
)
If that anonymous function starts getting too hairy to write, just make a method that accepts an IGrouping<int, Row> and turns it into what you want. Then call it like:
Dim data = result.Rows
.GroupBy( )
.Select( myMethod )