Passed function as param should use another param from the same call - javascript

I'm recreating the filter, map, find functions in js. And I have a function pipe, that takes the given array and passes it trough an array of functions such as filter, map etc.
These are my functions:
const filter = (arr, callback) => {
let newArr = [];
let j = 0;
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
newArr[j] = arr[i];
j++;
}
}
return newArr;
};
const map = (arr, callback) => {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
newArr[i] = callback(arr[i], i);
}
return newArr;
};
const pipe = (arr, callbacks) => {
console.log(arr, "arr");
console.log(callbacks, "callbacks");
};
I can use filter like so, and the function works fine.
const arr = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']
filter(arr, item => item.length > 6) // returns ["exuberant", "destruction", "present"]
However, when using pipe, I should not pass arr to each function. Instead filter and map should somehow take arr from pipe. But since those functions are being called directly in the pipe call, I do not see how can I achieve that functionality.
const arr = ['spray', 'limit', 'elite', 'exuberant', 'destruction']
pipe(
arr,
[
filter(item => item.length > 6), // returns ["exuberant", "destruction"]
map((item, index) => ({ id: index, name: item })) // returns [{ id: 0, name: 'exuberant' }, { id: 1, name: 'destruction' }]
]
)
// pipe returns [{ id: 0, name: 'exuberant' }, { id: 1, name: 'destruction' }]
Usually filter and map takes 2 params, arr and a callback. But in pipe it takes just the callback.
Any help would be appreciated.
Thanks.

I see that you are trying to implement a concept of functional programming known as pipe.
There's a better way to do what you are trying to achieve.
Instead of passing the array to the pipe function, only pass the map and filter functions to the pipe function and have pipe function return another function that takes the input array and returns the result of executing each function from left to right and passing the input array to each function.
Also change map and filter functions to return another function that takes in the input array as an argument and returns a new array after iterating over the array and passing each each element to the callback function.
const filter = callback => {
return (arr) => {
let newArr = [];
let j = 0;
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
newArr[j] = arr[i];
j++;
}
}
return newArr;
};
};
const map = callback => {
return (arr) => {
const newArr = [];
for (let i = 0; i < arr.length; i++) {
newArr[i] = callback(arr[i], i);
}
return newArr;
};
};
const pipe = (...funcs) => {
return array => {
return funcs.reduce((acc, curr) => curr(acc), array);
};
};
const arr = ['spray', 'limit', 'elite', 'exuberant', 'destruction'];
const result = pipe(
filter(item => item.length > 6),
map((item, index) => ({ id: index, name: item }))
)(arr);
console.log(result);

there is no easy solution for this since you call the function filter and map directly in the pipe creation (simple functions will execute then)
one thing you can do is change your filter and map functions so they return an object with a method .execute that needs to be called to execute the filter or the map
const filter = (callback) => {
function execute (arr) = {
let newArr = [];
let j = 0;
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
newArr[j] = arr[i];
j++;
}
}
return newArr;
}
return { // return the object ready to execute
execute
}
};
const map = (callback) => {
function execute (arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
newArr[i] = callback(arr[i], i);
}
return newArr;
}
return {
execute
}
};
now if you want to use a filter, do
const arr = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']
filter(item => item.length > 6).execute(arr)
// returns ["exuberant", "destruction", "present"]
and now the pipe function
const pipe = (arr, callbacks) => {
for (let i = 0; i < callbacks.length; i++) {
callback.execute(arr)
}
};
now use the pipe as wanted
const arr = ['spray', 'limit', 'elite', 'exuberant', 'destruction']
pipe(
arr,
[
filter(item => item.length > 6),
map((item, index) => ({ id: index, name: item }))
]
)
// returns [{ id: 0, name: 'exuberant' }, { id: 1, name: 'destruction' }]

Related

How do I remove multiple elements from an array?

I want to write a function that passes an array and an optional number of values to be removed from the array as parameters. My function works when there is only 1 value, but fails when there is multiple values.
const removeFromArray = function (arr, ...theArgs) {
for (let i = 0; i < arr.length; i++) {
if (theArgs.includes(arr[i])) {
arr.splice(i, 1);
}
}
return arr;
};
You can use the filter method for that:
const removeFromArray = function (arr, ...theArgs) {
return arr.filter( val => !theArgs.includes(val) )
};
const list = [1,2,3];
const newList = removeFromArray(list, 2,3);
console.log(newList);
And a more terse version:
const removeFromArray = (arr, ...args)=> arr.filter( val => !args.includes(val) )
Tip: try to avoid mutating the original array and work on or return a copy during these operations.
The issue is with your indexing, you are finding the element using the index of arr, and deleting in the array, which is probably causing issue with indexing in loop.
Modify your code as follows
const removeFromArray = function (arr, ...theArgs) {
for (let i = 0; i < theArgs.length; i++) {
if (arr.includes(theArgs[i])) {
arr.splice(arr.indexOf(theArgs[i]), 1);
}
}
return arr;
};
The above fixes the code your way, but a better way of doing it would be using filter.
const removeFromArray = function (arr, ...theArgs) {
return arr.filter(ele => !theArgs.includes(ele))
}
I am writing it this way to purely maintain your function.
The problem is because you remove item from the array while being looping from that array.
Every time your for loop iterate the array, it will get a new array
e.g. (1,2,3,4,5 => 2,3,4,5), but the i value just keeping increasing by 1.
const removeFromArray = function (arr, ...theArgs) {
for (let i = 0; i < arr.length; i++) {
console.log(`arr:${arr}`,`item${arr[i]}`,`num${i}`)
console.log(arr[i])
if (theArgs.includes(arr[i])) {
arr.splice(i, 1);
}
}
return arr;
};
const testarray = [1,2,3,4,5]
console.log(removeFromArray(testarray,1,2,3))
I would suggest to use array as a second parameter.
var array1 = ['a','b','c'];
var elementsToRemove = ['a','b'];
const removeFromArray = function (array1, elementsToRemove) {
var filtered = array1.filter(function(value, index, array){
return elementsToRemove.includes(value);
});
return filtered;
}
console.log(removeFromArray(array1,elementsToRemove));

replace nested for loop with array helpers - finding priority element

I am looking to replace nested for loop implementation with array helpers for optimization,
The function returns string from testArray, which contains the highest priority as per priorityArray.
const arr1 = ["A_COUNTY","B_STATE","C_COUNTRY", "D_CONTINENT","E_WORLD"];
const arr2 = ["X_COUNTY","Y_STATE","Z_PLANET"];
const priorityArray = ["CONTINENT","COUNTRY","STATE","COUNTY"];
function findPriorityElement(testArray:string[])
{
for (let i = 0; i <priorityArray.length; i++)
for (let j = 0; j <testArray.length; j++) {
if (testArray[j].includes(priorityArray[i]))
return testArray[j];
}
return "";
}
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"
Any leads is appreciated.
You could use find() instead of the inner for loop
const arr1 = ["A_COUNTY", "B_STATE", "C_COUNTRY", "D_CONTINENT", "E_WORLD"];
const arr2 = ["X_COUNTY", "Y_STATE", "Z_PLANET"];
const priorityArray = ["CONTINENT", "COUNTRY", "STATE", "COUNTY"];
function findPriorityElement(testArray) {
for (p of priorityArray) {
const match = testArray.find(e => e.includes(p));
if (match) {
return match;
}
}
}
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"
const arr1 = ["A_COUNTY", "B_STATE", "C_COUNTRY", "D_CONTINENT", "E_WORLD"];
const arr2 = ["X_COUNTY", "Y_STATE", "Z_PLANET"];
const priorityArray = ["CONTINENT", "COUNTRY", "STATE", "COUNTY"];
const findPriorityElement = testArray => {
for (const priorityElement of priorityArray) {
const elem = testArray.find(testElement => testElement.includes(priorityElement));
if (elem) {
return elem;
}
}
};
console.log(findPriorityElement(arr1)); // Result: "D_CONTINENT"
console.log(findPriorityElement(arr2)); // Result: "Y_STATE"

Inconsistency, when returning index of duplicate values

I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);

Transform elements of an array into objects / Javascript

I have a challenge to create a simple Notes manager in JS, I've written a function that takes one string, gives it and id and pushes it to an array of notes.
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
I now struggle with a function that will take multiple strings as parameters
('own', 'snail', 'platypus')
create an object for each element with id/value(string) and push it to the main array.
The result should look like:
[{ id: 1, value: 'owl'},
{ id: 2, value: 'snail'}]
So far I have this, it assigns ID correctly, but the loop fails
const batchAddNotes = (values) => {
let obj = {};
for (i = 0; i < values.length; i++) {
obj.id = (getId());
obj.value = (values[i]);}
return obj;};
To have your variables in a certain scope, I'd pack it all in a class (or a function). As you're using arrow functions, the class should be ok. To add multiple nodes the way you've shown; using var-args, you can create a method that expects those with (...input)
class Notes {
constructor() {
this.nextId = 0;
this.nodes = [{
id: this.getId(),
value: 'Note'
}];
}
addNote(input) {
this.nodes.push({
id: this.getId(),
value: input
})
}
getId() {
return this.nextId++;
}
addNotes(...input) {
input.forEach(e => this.addNote(e));
}
}
const notes = new Notes();
notes.addNotes('own', 'snail', 'platypus');
console.log(notes.nodes);
Use the functions arguments object. It's an array of all the arguments that are being passed to a function. Then you can loop over them and run your functionality on them each time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
You could use the arguments passed in your function as var args
const addNote = _ => {
for(var i = 0; i < arguments.length; i++){
notes.push({id:getId(), value: arguments[i]});
console.log('Note added');
}
}
use rest params :
const myFn = (...values) => {
let tmpArr = [];
for(let i = 0 ; i < values.length ; i++){
tmpArr.push({
id : i + 1,
value : values[i]
});
}
return tmpArr;
}
const result = myFn('own', 'snail', 'platypus');
console.log(result);
This is how it look like when using Rest Params and reusing your first function. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
You can add so many notes as you want (addMultipleNotes can receive indefinite number of arguments )
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addSingleNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
};
const addMultipleNotes = (...args) => {
for(let i = 0; i < args.length; i++){
addSingleNote(args[i]);
}
};
addMultipleNotes('one', 'two', 'three');
console.log(notes);
First of all, note how I've used an IIFE and a closure to create an id generator.
In the other hand, rest parameters, Array#map and parameter spread are your friends:
const incrId = (() => {
let id = 0
return () => ++id
})()
const valuesToNotes = (...values) => values.map(value => ({
id: incrId(),
value
}))
const notes = []
// Parameter spread (i.e. '...') gives each
// array item in the output of valuesToNotes
// as if you would use Function#apply
notes.push(...valuesToNotes('a', 'b', 'c'))
console.log(notes)
Yet another more functional approach which doesn't mutate the input notes and produces a new one with existing notes plus the ones transformed from values:
const concat = xs => ys => xs.concat(ys)
const map = f => xs => xs.map(f)
const pipe = xs => x => xs.reduce((r, f) => f(r), x)
const incrId = (() => {
let id = 0
return () => ++id
})()
const valueToNote = value => ({
id: incrId(),
value
})
const notes = []
const appendNotes = pipe([map(valueToNote), concat(notes)])
const moreNotes = appendNotes(['a', 'b', 'c'])
console.log(moreNotes)

JavaScript ES6 - count duplicates to an Array of objects

I'm creating for my list of products a filter to count all producers and display like this:
Apple (3)
I eliminated the duplicates from array: ["Apple","Apple","Apple"] I used this link:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
But my problem is that I want to count these elements from array and display them in an Array of Objects cause i need to iterate it later.
From this Array of Apples above i need result: [{"Apple": 3},{...},{...}]
I was trying to do this but it returns me object and I can't iterate after it:
How to count duplicate value in an array in javascript
I need an Array of Objects it's not duplicated
I'm using Angular 4.
My code:
component.ts
async ngOnInit() {
this.cart$ = await this.cartService.getCart();
this.subscription = this.productService.getAll().subscribe(products => {
this.category = products.filter(
products => products.category == this.name
);
this.filters();
});
}
filters() {
this.category2 = this.category.map(value => value.producer);
this.filteredArray = this.eliminateDuplicates(this.category2);
console.log(this.filteredArray);
}
eliminateDuplicates(arr) {
let i,
len = arr.length,
out = [],
obj = {};
for (i = 0; i < len; i++) {
obj[arr[i]] = 0;
}
for (i in obj) {
out.push(i);
}
return out;
}
component.html
<div *ngFor="let f of filteredArray">
{{f}}
</div>
You can use reduce to summarize the array and map for form the desired output
let obj = ["Apple", "Apple", "Apple", "Orange"];
let result = Object.values(obj.reduce((c, v) => {
c[v] = c[v] || [v, 0];
c[v][1]++;
return c;
},{})).map(o=>({[o[0]] : o[1]}));
console.log(result);
Here:
const array = ["a", "a", "b"]
const result = { }
for (let i = 0; i < array.length; i++) {
result[array[i]] = (result[array[i]] || 0) + 1
}
Object.keys(result).map(key => ({ [key]: result[key] }))
That last line is the key for
I was trying to do this but it returns me object
you can simply do it by using Lodash countBy function
filters() {
this.category2 = this.category.map(value => value.producer);
this.filteredArray = _.countBy(this.category2);
console.log(this.filteredArray);
// Object {Apple: 3, Orange: 1}
}
You can simply do it by using array.reduce() method
const votes = ['Yes', 'Yes', 'Yes', 'No', 'No', 'Absent'];
const result = votes.reduce((prevValue, vote) => {
if (prevValue[vote]) {
prevValue[vote]++;
} else {
prevValue[vote] = 1;
}
return prevValue;
}, {});
console.log(result);
Output : { Yes: 3, No: 2, Absent: 1 }

Categories