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)
Related
I using chart package for data analystics
Here is my code:
const param = ['Country', 'Device_Model', 'MMA', 'Game'] // other parameters i want to use eg: Game (current)
let arr = []
const handleChangeParam = () => {
for (let i = 0; i < context.gameAdData.length; i++) {
context.gameAdData[i].map((element) =>
arr.push(element._fieldsProto.!!!Game?!!!.stringValue) // Here is parameter Game
)
}
return arr
}
here is ss:
I need to replace the 'Game' parameter that I specified in the exclamation point with other parameters in the first line.
The image that comes to my mind is as follows, I click the buttons and pull the relevant data
const param = ['Country', 'Device_Model', 'MMA', 'Game']
let arr = []
const handleChangeParam = () => {
for (let i = 0; i < context.gameAdData.length; i++) {
context.gameAdData[i].map((element) =>
arr.push(element._fieldsProto.[param[i]]?.stringValue)
)
}
return arr
}
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' }]
I have some code that is working but I want to abstract it into a functional component I can use elsewhere in my script. I am getting an undefined error:
This works:
//add an index to each element
var items = learning_check.map(function(el,i) {
var o = Object.assign({}, el);
o.key = i;
return o;
});
this doesn't:
const addIndex = (a) => {
console.log('addIndex initiated')
a.map = (el,i) => {
var o = Object.assign({}, el);
o.key = i;
return o;
}
}
called with
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + mItems); // undefined
Firstly, I'd like to say that you're on the right track. You're missing two things.
You need to call map and not reassign it, like a.map(...) and not a.map = .... And, you need to return the result of map from your addIndex function. Like so,
const addIndex = (a) => {
console.log('addIndex initiated');
// Notice the return
return a.map((el, i) => { // See how we call map here
var o = Object.assign({}, el);
o.key = i;
return o;
});
}
// Mock
const learning_check = [{
id: "abcde"
}, {
id: "fghij"
}];
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + JSON.stringify(mItems));
I'll suggest a simplification to your code here, you could use it if you like
const addIndex = (a) => {
console.log('addIndex initiated')
return a.map((el, i) => Object.assign({
key: i
}, el));
}
// Mock
const learning_check = [{
id: "abcde"
}, {
id: "fghij"
}];
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + JSON.stringify(mItems));
Two bugs in your code
1) You are not returning anything from the function.
2) You are not calling the map function.
const addIndex = (a) => {
console.log('addIndex initiated')
return a.map((el, i) => {
var o = Object.assign({}, el);
o.key = i;
return o;
})
}
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 }
I am trying to add an object to an array if the array already does not have that object.
So I have an array as follows
[{id:1},{id:2},{id:3}]
I want to check if a id:1 exist or not if not then add if yes then show an error or log a message.
I am able to achieve this using a simple array as follows.
let result =[1,2,2,3,1,4,1,4,2,3].filter((el, i, a) => i === a.indexOf(el));
I cannot figure out how to achive the same with array of objects.
Thanks
You can use some to check for duplicates like:
// array with duplicate objects {id:1}
let arr = [{id:1},{id:1},{id:2}]
function duplicateFound(arr){
const ids = arr.map(x => x.id);
return ids.some((item, idx) => ids.indexOf(item) != idx);
}
console.log(duplicateFound(arr));
// array with not duplicates
arr = [{id:1},{id:2},{id:3}]
console.log(duplicateFound(arr));
You can use Array#filter, and check the length:
const arr = [{id:1},{id:2},{id:3}];
const el = { id: 1 };
const exists = arr.filter(({ id }) => id === el.id).length > 0;
console.log(exists);
Or you can use Array#find, which has a slight advantage over Array#filter, since it will stop as soon as an item was found.
const arr = [{id:1},{id:2},{id:3}];
const el = { id: 1 };
const exists = !!arr.find(({ id }) => id === el.id);
console.log(exists);
You can wrap your array with a proxy that has a set trap, to prevent the insertion of duplicates automatically:
const arr = [{id:1},{id:2},{id:3}];
const arrayChangeHandler = {
set: function(target, property, value, receiver) {
if(property === 'length') {
return true;
}
const exists = !!target.find(({ id }) => id === value.id);
if(exists) {
console.log(`Id: ${value.id} exists!`); // you can return false here, and it will throw an error
} else {
target.push(value);
}
return true;
}
};
const pArr = new Proxy(arr, arrayChangeHandler);
pArr.push({ id: 1 });
pArr.push({ id: 10 });
console.log(JSON.stringify(arr));
You could try inserting all values as keys to a new array then flip keys & vals
let arr = "abccba".split('');
let res = [];
arr.forEach((n) => {
res[n] = n;
});
console.log(Object.keys(res));
A concern might be that if your values are numbers then you might need to recast them eg.
res = res.map(n) => +n