Group by array with first letter in javascript - javascript

I Have array like
['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha',
'Kamal', 'Bavi']
I want ouptut like
['A',
'Ajith',
'Arya',
'Akila',
'B',
'Bharath',
'Bhakya',
'Bavi',
'k',
'Kamal']
how can i acheive this in javascript
Help me and thanks advance

You can simply do as your title says — 'group-by' first letter, then map the result.
const input = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi',];
const grouped = {};
for (const word of input) {
(grouped[word[0].toUpperCase()] ??= []).push(word);
}
const result = Object.entries(grouped).flatMap(([k, vs]) => [k, ...vs]);
console.log(result);

Can reduce() everything to an object, indexed by the first letter:
const data = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya',
'Kavitha', 'Kamal', 'Bavi'];
const result = [...data].sort().reduce((a, v) => {
const [first] = v;
a[first] ??= [];
a[first].push(v);
return a;
}, {});
console.log(result);
This object will have the keys in the correct order because sort() was called first on (a copy of) the array, and string keys are maintained in insertion order.
If you want to convert that object to a flat array, you can just do:
const array = Object.keys(result).flatMap((k) => [k, ...result[k]]);

Here's a solution, probably not the most optimized one, but it should work :
The idea is to sort the array, loop over all the elements to compare the first letter in order to create a subgroup title.
It would look something like this :
var displaySortedArray = function(array) {
var sortedArray = array.sort();
console.log(sortedArray[0].charAt(0));
console.log(sortedArray[0]);
for(let index = 1 ; index < sortedArray.length ; index++){
if(sortedArray[index].charAt(0) != sortedArray[index-1].charAt(0)){
console.log(sortedArray[index].charAt(0));
}
console.log(sortedArray[index]);
}
}
displaySortedArray(['Test','SUUUU','Easy','Try2','Elements','Super','Amazing']);
This function is an example and prints what you asked for, however i can't tell you how to implement this where you need it.
Have a good day.

You could group by first uppercase character with the character and all following items in a flat array.
const
data = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'],
result = Object
.values(data.reduce((r, s) => {
const key = s[0].toUpperCase();
(r[key] ??= [key]).push(s);
return r;
}, {}))
.sort(([a], [b]) => a.localeCompare(b))
.flat();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

This is possible to do using a single reduce call which I find quite elegant:
const data = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'];
console.log(
data
.sort()
.reduce(
(items, item, index, array) => {
console.log({ prev: array[index - 1], current: item });
if (array[index - 1]?.[0] !== item[0]) {
items.push(item[0]);
}
items.push(item);
return items;
},
[]
)
);
Codepen

Something like this? Seems simpler than what was posted so far
const arr = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi']
const newArr = arr
.slice() // copy
.sort();
let letter = "";
for (let i=0; i<newArr.length; i++) {
const thisLetter = newArr[i].charAt(0);
if (thisLetter !== letter) {
letter = thisLetter;
newArr.splice(i,0,thisLetter)
}
}
console.log(newArr)

Several good answers are already here. This one may be a little bit simpler:
const arr = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'].sort();
for(let i = arr.length -1; i>=0; i--) {
if(i === 0 || arr[i][0] !== arr[i-1][0]) arr.splice(i,0, arr[i][0]);
}
console.log(arr);

let d=["Ajith","Arya","Akalya","Akila",
"Bharath","Bhakya","Kavitha","Kamal","Bavi"].sort()
let r=[...new Set(d.map(([c])=>c))].flatMap(c=>[c, ...d.filter(([i])=>i==c)])
console.log(r)
or:
let d = ["Ajith","Arya","Akalya","Akila",
"Bharath","Bhakya","Kavitha","Kamal","Bavi"].sort()
let r = Object.values(d.map(s=>
[s.charAt(0),s]).reduce((c,[a,b])=>((c[a]??=[a]).push(b),c),{})).flat()
console.log(r)

You can use map
let names = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'];
let map = new Map();
names.forEach(item => {
let firstLetter = item[0].toUpperCase();
if (!map.has(firstLetter)) {
map.set(firstLetter, [firstLetter]);
}
map.get(firstLetter).push(item);
});
let result = [];
map.forEach(value => {
result.push(...value);
});
console.log(result);
You can also make every letter a separate group
let name = ['Ajith', 'arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'];
let map = new Map();
name.forEach(item => {
let firstLetter = item[0].toUpperCase();
if (!map.has(firstLetter)) {
map.set(firstLetter, [firstLetter]);
}
map.get(firstLetter).push(item);
});
let result = [];
map.forEach(value => {
result.push(value);
});
console.log(result);

let names = ['Ajith', 'Arya', 'Akalya', 'Akila', 'Bharath', 'Bhakya', 'Kavitha', 'Kamal', 'Bavi'];
let result = [];
let firstLetters = new Set();
for (let i = 0; i < names.length; i++) {
firstLetters.add(names[i][0]);
}
firstLetters = Array.from(firstLetters).sort();
for (let i = 0; i < firstLetters.length; i++) {
result.push(firstLetters[i]);
for (let j = 0; j < names.length; j++) {
if (names[j][0] === firstLetters[i]) {
result.push(names[j]);
}
}
}
console.log(result);

Related

How to add an element to an array inside an array in JS ES6?

For example, I know I can use spread operator to add an element to an array in ES6.
const arr = ["a","b"]
const newArr = [...arr, "c"]
that I get
["a","b","c"]
But how to implement this in an nested array, like
const arr = [["a","b"], ["d"]]
I want to get
newArr1 = [["a","b","c"], ["d"]]
and
newArr2 = [["a","b"],["d","c"]]
Perhaps we can use
[[...arr[0], 'c'], [...arr[1]]]
but what if
const arr = [["a","b"], ["d"],["e"]]
and I still want
[["a","b","c"], ["d"],["e"]]
is there a common way?
You can simply use the array index or use findIndex and find methods, in case you want to remove not based on the index
const arr = [[a,b], [d]]
arr[1].push(c)
//arr = [[a,b], [c,d]]
const arr = [['a', 'b'], ['d']]
const array = [...arr]
const arrayIndex = array.findIndex(item => item.find(i => i === 'd'))
if (arrayIndex !== -1) {
const oldArray = array.find(item => item.find(i => i === 'd'))
const newArray = [...oldArray, 'c']
array[arrayIndex] = newArray
}
console.log(array);

How to match duplicate values in nested arrays in JS

I have an array which contains nested arrays that are dynamically created, it looks like this:
[['1', '2'],['1','3', '4'],['1', '3']]
I am trying to implement AND logic by getting only duplicate values from these arrays. My expected output here would be ['1'] since all nested arrays must contain the same value.
// [['1', '2'],['1','3', '4'],['1', '3']]
const arrays = [...new Set(response)].filter(newSet => newSet.length > 0);
const builder = []; // array of all the id's no longer needed
// [[],[],[]]
arrays.forEach(arr => {
// []
arr.forEach(value => {
// [[], [], []]
const found = arrays.filter(a => a.find(v => v === value));
if (found.length === 0)
builder.push(value);
});
});
console.log(builder); // empty []
This gives me an empty array because of the filter(). How could I return an array of values that all (3 in this case but could be more) arrays contain?
Expected output with the above input would be ["1"]. Any help appreciated.
from what I understand you need the common elements from all array
let response1 = [['1', '2'],['1','3', '4'],['1', '3']]
let response2 = [['1', '2', '3'],['1','3', '4'],['1', '3']]
const getCommon = res => [...new Set(res.flat())].filter(a => res.every(c => c.includes(a)));
console.log(getCommon(response1))
console.log(getCommon(response2))
UPDATE
Apparently the set transformation is unnecessary since anyway it has to give the elements common to every array so filtering from res[0] should do
let response1 = [['1', '2'],['1','3', '4'],['1', '3']]
let response2 = [['1', '2', '3'],['1','3', '4'],['1', '3']]
const getCommon = res => res[0].filter(a => res.every(c => c.includes(a)));
console.log(getCommon(response1))
console.log(getCommon(response2))
You can make a count object that has the frequency of each number in it, and just check if the frequency of a number is equal to the length of the original array.
const getIntersectVals = (arrayOfVals)=>{
const freqs = {};
for(let arr of arrayOfVals){
for(let val of arr){
if(freqs[val]) freqs[val]++;
else freqs[val] = 1;
}
}
const uniqueVals = Object.keys(freqs);
const correctVals = uniqueVals.filter(elem=>{
return freqs[elem] === arrayOfVals.length;
})
return correctVals;
}
const arrayOfVals = [['1', '2'],['1','3', '4'],['1', '3']];
console.log(getIntersectVals(arrayOfVals))
Lodash intesection if you don't mind
const arrayOfVals = [['1', '2'],['1','3', '4'],['1', '3']];
const result = _.intersection(...arrayOfVals);
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>

Update value of object with values in different array

I have two array with below structure:
Array1:
[{id:1234,name:"macaron",quantity:"330gm",rate:"100"},{id:5678,name:"Gelato",quantity:"450gm",rate:"200"}]
Array2:
[{id:1234,name:"macaron",quantity:"600gm",rate:"300"},{id:5678,name:"Gelato",quantity:"800gm",rate:"500"}]
result
Array1 =[{id:1234,name:"macaron",quantity:"330gm",rate:"300"},{id:5678,name:"Gelato",quantity:"450gm",rate:"500"}]
I want to be able to update the only rates of objects in Array1 with rate of objects in Array2.
If order or lengths of the arrays are different one efficient way is
create a Map of the new rates from Array2 then loop over Array1 ant get() from the Map.
This way you only iterate Array2 once instead of using multiple iterations of methods like find()
let Array1=[{id:1234,name:"macaron",quantity:"330gm",rate:"100"},{id:5678,name:"Gelato",quantity:"450gm",rate:"200"}],
Array2=[{id:1234,name:"macaron",quantity:"600gm",rate:"300"},{id:5678,name:"Gelato",quantity:"800gm",rate:"500"}];
const rateMap = new Map(Array2.map(({id, rate})=> [id, rate]));
Array1.forEach(e=> rateMap.has(e.id) && (e.rate = rateMap.get(e.id )))
console.log(Array1)
let Array1 = [
{id:1234,name:"macaron",quantity:"330gm",rate:"100"},
{id:5678,name:"Gelato",quantity:"450gm",rate:"200"}
];
let Array2 = [
{id:1234,name:"macaron",quantity:"600gm",rate:"300"},
{id:5678,name:"Gelato",quantity:"800gm",rate:"500"}
];
for (i = 0; i < Array1.length ; i++) {
Array1[i].rate = Array2[i].rate
}
console.log(Array1)
Array1 = Array1.map(item => {
return { ...item, rate: Array2.filter(rec => rec.id === item.id)[0].rate };
});
});
The cleanest solution I came up with.
But as charlietfl said: I make assumtion that both lengths are the same and in same order
let array1 = [{id:1234,name:"macaron",quantity:"330gm",rate:"100"},{id:5678,name:"Gelato",quantity:"450gm",rate:"200"}]
let array2 = [{id:1234,name:"macaron",quantity:"600gm",rate:"300"},{id:5678,name:"Gelato",quantity:"800gm",rate:"500"}]
array1.updateRate = function(array2) {
for(let i = 0; i < this.length;i++) {
console.log(this[i].rate)
this[i].rate = array2[i].rate;
console.log(this[i].rate)
}
}
array1.updateRate(array2);
console.log(array1)
Here is second (full) solution, where that asumption isn't made:
let array1 = [{id:1234,name:"macaron",quantity:"330gm",rate:"100"},{id:5678,name:"Gelato",quantity:"450gm",rate:"200"}]
let array2 = [{id:1234,name:"macaron",quantity:"600gm",rate:"300"},{id:5678,name:"Gelato",quantity:"800gm",rate:"500"}]
array1.updateRate = function(array2) {
for(let i = 0; i < this.length;i++) {
for(let j = 0; j < array2.lenght;j++) {
if(this[i].id === array2[j].id) {
this[i].rate = array2[j].rate;
break;
}
}
}
}
array1.updateRate(array2);
console.log(array2)

JS reduce: TypeError: Cannot create property at Array.reduce

I have the following:
let keys = ['a','b','c'];
let vals = ['1','b','h'];
const my_obj = keys.reduce(function(acc,key,i) {
return acc[key]=vals[i];
},{});
logger.log(my_obj);
I would like my_obj to be {a:1,b:b,c:h}.
I'm getting:
TypeError: Cannot create property 'b' on string '1'
at Array.reduce
What am I doing wrong?
If you want to use reduce method then its fine, but you will have to return the accumulator to make it work.
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
const my_obj = keys.reduce(function(acc, key, i) {
acc[key] = vals[i];
return acc;
}, {});
console.log(my_obj);
But I will suggest to use the simple for loop, because of just the simplicity of problem
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
let my_obj = {};
for (let i = 0; i < keys.length; i++)
my_obj[keys[i]] = vals[i];
console.log(my_obj);
You need to return object as acc.
In your code you are returning the assigned value
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
const my_obj = keys.reduce(function(acc, key, i) {
acc[key] = vals[i];
return acc;
}, {});
console.log(my_obj);
return acc[key]=vals[i]; actually return the value of vals[i] ==> you finally get h only on https://playcode.io/ environment.
Updated: in your environment, you got that error because:
after first run of keys.reduce your next acc will be "1" (return acc[key]= vals[0]} equal return vals[0] which is "1")
Now you come to second round of keys.reduce, your acc is "1" then you set acc[key]= vals[1]} equal "1"[b] = vals[1] ==> you got that error
Try this:
let keys = ['a','b','c'];
let vals = ['1','b','h'];
const my_obj = keys.reduce(function(acc,key,i) {
// return new expanded acc
return ({...acc, [key]: vals[i]})
},{});
console.log(my_obj);

Mixing Arrays using JavaScript

What's the best way to mix multiple arrays like the way in the image below,
PS:
I don't know what will be the length of each array
Arrays will contain +10000 elements
There will be more than 3 arrays
I made a solution for it but I'm looking for any better solution
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
You could use lodash for your solution.
const { flow, zip, flatten, filter} = _
const doTheMagic = flow(
zip,
flatten,
filter
)
const one = [1, 2, 3]
const two = ['😕', '🤯', '🙈', '🙊', '🙉', '😃']
const three = ['foo', 'bar', 'wow', 'okay']
const result = doTheMagic(one, two, three)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Works with different length of arrays and makes use of functional programming for elegant code.
Here's a codepen to run: https://codepen.io/matteodem/pen/mddQrwe
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
let a1 = [1, 2, 3, 4, 5];
let a2 = ["🏯", "🏜", "🏭", "🎢", "🌠", "🏗"];
let a3 = ['one', 'two', 'three', 'four', 'five'];
const doTheMagic = arrayOfArrays => {
let maxLength = 0;
let result = [];
for (arr in arrayOfArrays) {
maxLength = Math.max(maxLength, arrayOfArrays[arr].length);
}
for (let i = 0; i < maxLength; i++) {
for (let j = 0; j < arrayOfArrays.length; j++) {
if (arrayOfArrays[j][i]) {
result.push(arrayOfArrays[j][i]);
}
}
}
return result;
}
console.log(doTheMagic([a1, a2, a3]));
This works with an unknown number of arrays, each one of unknown length :
const arrays = [
[1, 2, 3, 4],
["a", "b", "c", "d", "e"],
["#", "#", "?"]
];
let output = [];
while (arrays.some(a => a.length)) { // While any of the arrays still has an element in it, keep going
for (let a of arrays) {
if (!a.length) continue;
output.push(a.shift()); // remove the first element of the array and add it to output
}
}
console.log(output)
This is my approach to achieve that, one for loop can make it. This will work if you don't know the number of arrays and array length as well.
function doTheMagic(arr){
let ans = [];
let maxLength = -1;
arr.forEach((tempArr)=>{
if(tempArr.length > maxLength){
maxLength = tempArr.length;
}
})
let l1=0,l2=0,l3=0;
for(let i=0;i<maxLength;i++){
arr.forEach((tempArr)=>{
if(tempArr[i]){
ans.push(tempArr[i]);
}
})
}
return ans;
}
let a1 = [1,2,3,4,5];
let a2 = ["🏯","🏜","🏭","🎢","🌠","🏗"];
let a3 = ['1','2','3','4','5'];
console.log(doTheMagic([a1,a2,a3]));
Not sure if this is the better, but how you can write code that handles any number of arrays passed in.
const weave = (...args) => // convert arguments to an array
args.reduce((res, arr, offset) => { // loop over the arrays
arr.forEach((v, i) => res[offset + i * args.length] = v) // loop over array and add items to their indexes
return res
}, []).filter(x => x !== undefined) // remove the unused indexes
const one = [1, 2, 3]
const two = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
const three = ['w', 'x', 'y', 'z']
const result = weave(one, two, three)
console.log(result)
const result2 = weave(one, two)
console.log(result2)
const result3 = weave(one, two, three, ['*', '&'])
console.log(result3)

Categories