JavaScript - array, object - How can I streamline this code - javascript

How can I streamline this code ?
I think it might have better way to refactor .
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = { ...bb }
cc.first = aa[cc.first]
cc.second = aa[cc.second]
cc.third = aa[cc.third]

You can use .reduce() and Object.entries() methods:
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = Object.entries(bb)
.reduce((r, [k, i]) => (r[k] = aa[i], r), {});
console.log(cc);

This is a solution that still works after adding or removing an item from aa or bb, without need to change your code.
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = {};
for(var key in bb)
cc[key] = aa[bb[key]];

Related

Sorting map by key returns sorted Map object with the first key in last index

The following function creates a Map object from two arrays. I am trying to sort the map by key into a new Map object by using the sort method, as shown on Mozilla Docs. The mapSort object is correctly sorting all of the keys, besides the fact that it is putting the first key at the last index.
const sortMapByKey = () => {
const keyArray = [4, 6, 16, 18, 2]
const valueArray = [103, 123, 4444, 99, 2000]
const buildMap = (keys, values) => {
const map = new Map();
for(let i = 0; i < keys.length; i++){
map.set(keys[i], values[i]);
};
const mapSort = new Map([...map.entries()].sort(function(a, b) {
return a - b;
}));
return mapSort
};
return buildMap(keyArray, valueArray)
}
The current output:
Map { 4 => '103', 6 => '123', 16 => '4444', 18 => '99', 2 => '2000' }
The desired output:
Map { 2 => '2000', 4 => '103', 6 => '123', 16 => '4444', 18 => '99' }
Any clue on what I'm doing wrong with my sort?
The entries are a key-value pair, eg [1, 103]. Doing return a - b tries to subtract two such arrays from each other, which doesn't make sense - you need to extract the key from the entry pair first.
const mapSort = new Map([...map.entries()].sort(function(entryA, entryB) {
return entryA[0] - entryB[0];
}));
const keyArray = [4, 6, 16, 18, 2]
const valueArray = [103, 123, 4444, 2000]
const buildMap = (keys, values) => {
const map = new Map();
let objectArray = []
for (let i = 0; i < keys.length; i++) {
map.set(keys[i], values[i]);
};
const mapSort = new Map([...map.entries()].sort(function(entryA, entryB) {
return entryA[0] - entryB[0];
}));
return mapSort
};
console.log(buildMap(keyArray, valueArray));
or destructure:
const mapSort = new Map(
[...map.entries()]
.sort(([a], [b]) => a - b)
);
const keyArray = [4, 6, 16, 18, 2]
const valueArray = [103, 123, 4444, 2000]
const buildMap = (keys, values) => {
const map = new Map();
let objectArray = []
for (let i = 0; i < keys.length; i++) {
map.set(keys[i], values[i]);
};
const mapSort = new Map(
[...map.entries()]
.sort(([a], [b]) => a - b)
);
return mapSort
};
console.log(buildMap(keyArray, valueArray));

remove specific element from all arrays inside object

I want to remove all 'main-1-3' elements inside arrays:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for(let i = 0; i < colors.length; i++) {
a[colors[i]].splice(a[colors[i]].indexOf('main-1-3'), 1);
}
console.log(a)
But as you see if there is no such element other elements are removed too! I want to only remove the 'main-1-3' in all of the arrays?
How can I fix this?
You should first check that the item really is in the array. indexOf an item that isn't in the array will return -1, which will mess up the splice call:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for(let i = 0; i < colors.length; i++) {
const index = a[colors[i]].indexOf('main-1-3');
if (index >= 0) {
a[colors[i]].splice(index, 1);
}
}
console.log(a)
You could just loop over a's Object.entries and filter out all the main-1-3 elements.
const a = {
white: ['main-1-1'],
red: ['main-1-3', 'main-1-1'],
orange: [],
green: [],
blue: ['main-1-2'],
}
for (let [key, arr] of Object.entries(a)) {
a[key] = arr.filter(el => el !== 'main-1-3');
}
console.log(a);
iterate through each array and filter out the item main-1-3:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
colors.forEach(e => a[e] = a[e].filter(n => n !== 'main-1-3'))
console.log(a)
Just check whether string 'main-1-3' exist in arrays in a.
Because if the string main-1-3 doesnot exist in the arrays in object a, the index will return -1.
Array.splice(-1, 1) will delete the last node in the array, this is what happening in your case.
Array.splice(-1, 1) Example
const myArray = [1, 2, 3, 4];
myArray.splice(-1, 1);
console.log(myArray); // Last element removed
As you see in the above example, Array.splice(-1, 1) deletes the last node. So you have to ensure that your index a[colors[i]].indexOf('main-1-3') is not -1 or else the last nodes in your array will be deleted.
Working Fiddle
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for (let i = 0; i < colors.length; i++) {
const index = a[colors[i]].indexOf('main-1-3');
if (index > -1) {
a[colors[i]].splice(index, 1);
}
}
console.log(a)

Reduce an array to groups

I have this array:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
I would like to reduce it to this:
['blueblue', 'green', 'blue', 'yellowyellow', 'green']
Where it concats the values if they are the same. Once the value changes it starts again.
Trying to use reduce but not sure how to make it work as the acc needs to be a string and an array depending on if the value is the same or not!
let lastType = vals[0]
const groups = vals.reduce((acc, value) => {
if (lastType === value) {
acc += value // string
}
lastType = value
return acc.push(value)
}, [])
The final result is an array, so that is what acc should be.
Instead of appending value to acc, append it to the last element of the array:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
let lastType = null;
const groups = vals.reduce((acc, value) => {
if (lastType === value) {
acc[acc.length - 1] += value;
} else {
lastType = value
acc.push(value)
}
return acc;
}, [])
console.log(groups);
The use of lastType in a closure is a bit of a code smell, as is the use of mutability in the reducer.
A preferable, though slightly more verbose approach:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
const { groups } = vals.reduce(({ lastType, groups }, value) => {
if (lastType === value) {
return {
lastType,
groups: [
...groups.slice(0, groups.length - 2),
groups[groups.length - 1] + value
],
};
}
return {
lastType: value,
groups: [...groups, value],
};
}, { groups: [], lastType: null })
console.log(groups);
You need to check the element at the index in front of the actual index and add a new string to the accumulator.
const
values = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green'],
result = values.reduce((accumulator, value, index, array) => {
if (value === array[index - 1]) accumulator[accumulator.length - 1] += value;
else accumulator.push(value);
return accumulator;
}, []);
console.log(result);
Push an empty string to the accumulator if the type is new. Remember that .push returns the new length of the array, so don't return it at the bottom of the function - instead, return the whole accumulator:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
let lastType;
const groups = vals.reduce((acc, value) => {
if (lastType !== value) {
acc.push('');
lastType = value;
}
acc[acc.length - 1] += value;
return acc;
}, [])
console.log(groups);
You need to keep track of a bit more. Basic way of doing it is using an bject to hold the state.
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
const groups = vals.reduce((acc, value, index, array) => {
// is it the same, duplicate it
if (acc.lastType === value) {
acc.current += value;
} else {
// did we have a previous value? Add it to the array
if (acc.lastType) {
acc.result.push(acc.current);
}
// set the current type
acc.current = value;
acc.lastType = value;
}
// if we are at the end, add what we have to the array
if (index+1===array.length) {
acc.result.push(acc.current);
}
return acc;
}, { result: [], current: '', lastType: null }).result;
console.log(groups);

Initialize object with type [key, value: Array] in shorthand form

Suppose I want to loop through an array of characters and build up an object which represents the frequency of each character, for example:
const frequency = {};
const str = 'stackoverflow';
for (let i = 0; i < str.length; i++) {
frequency[str[i]] = (frequency[str[i]] + 1) || 1;
}
With the above, we would expect an object in the form of:
{
s: 1,
t: 1,
a: 1,
c: 1,
k: 1,
o: 2,
v: 1,
e: 1,
r: 1,
f: 1,
l: 1,
w: 1
}
Now suppose I wanted to loop through an array of nested arrays, each with a form of [id, val]. Can I create an object such that I would end up with multiple keys, each representing a different id, and a corresponding array, filled with the values, in the same shorthand form as above?
For example:
[ [1,2], [1,3], [1,5], [2,7], [3,0], [1,10] ]
{
1: [2,3,5,10],
2: [7],
3: [0]
}
Is there something similar to:
const map = {};
for (let i = 0; i < list.length; i++) {
map[list[i][0]] = (map[list[i][0].push(map[list[i][1])) || [list[i][1]];
}
Functional approach with .reduce():
const list = [[1,2],[1,3],[1,5],[2,7],[3,0],[1,10]]
const x = list.reduce((a, [k, v]) => (a[k] = [...a[k] || [], v], a), {})
console.log(x)
Edit: stole #jered's much better conditional instead of ternary
Classic use case for a reducer:
const arr = [ [1,2], [1,3], [1,5], [2,7], [3,0], [1,10] ];
const map = arr.reduce((acc, cur) => {
return {
...acc,
[cur[0]]: [...(acc[cur[0]] || []), cur[1]]
};
}, {});
Of course, it could be a lot more readable than this... but computed property names and the spread syntax make it pretty slick and compact (it'd fit on a single line if you wanted to).
Edit: ooo I like #woat's use of single character var names and destructuring assignment in the function signature :) We could throw that in too:
const arr = [ [1,2], [1,3], [1,5], [2,7], [3,0], [1,10] ];
const map = arr.reduce((a, [k, v]) => ({...a, [k]: [...(a[k] || []), v]}), {});
const list = [ [1,2], [1,3], [1,5], [2,7], [3,0], [1,10] ];
const map = {};
for (let item of list) {
map[item[0]] = [...map[item[0]] || [], item[1]];
}
console.log(map);

Javascript - Index Each Element in an Array/List

> var lst = ['red', 'blue', 'yellow'];
> someFunc(lst);
[('red', 0), ('blue', 1), ('yellow', 2)]
Is there any way to do this in Javascript/JQuery? I know that I can simply just make a for loop and convert each of the original list's element to what I want, but I was wondering if there was a built in way of doing this.
Thanks!
You could use Array#map and return for each item an array with the value and the index.
var lst = ['red', 'blue', 'yellow'],
array = lst.map(function (a, i) { return [a, i]; });
console.log(array);
You can leverage the map() function that is providing you both the value and and index of each item in the array:
lst.map(function(v, i) { return { value: v, index: i }; } );
See MDN
With map method in ES6 :
var lst = ['red', 'blue', 'yellow'];
var array = lst.map((item, index) => [item, index])
console.log(array); // [ [ 'red', 0 ], [ 'blue', 1 ], [ 'yellow', 2 ] ]

Categories