ramda array cleaning data to produce a unique array - javascript

I am looking to use Ramda to take some data - extract a key value from it - and reduce it so the array is unique
so in this case - create an array ["SONY_FINALIZING", "EXPIRE"];
-- as an extra - I would like to create other functionality to lowercase the values, add hyphens, camel case words.
trying to use this but can't seem to share a fiddle
https://ramdajs.com/repl?v=0.26.1
const data2 = [
{id: 38,
label: "ssss",
status: "SONY_FINALIZING",
region: "SIEA"},
{id: 35,
label: "ghmjhmjhj",
status: "SONY_FINALIZING",
region: "SIEE"},
{id: 32,
label: "gbfghfghfghg",
status: "EXPIRE",
region: "SIAE"}
]
pipe(
groupBy(prop('id')),
map(pluck('status')),
map(flatten),
map(uniq),
)(data2)

Create a function using R.pipe that uses R.pluck to get an array of status values, and then R.uniq to remove the duplicates:
const {pipe, pluck, uniq} = R;
const fn = pipe(pluck('status'), uniq);
const data2 = [{ id: 38, label: "ssss", status: "SONY_FINALIZING", region: "SIEA" }, { id: 35, label: "ghmjhmjhj", status: "SONY_FINALIZING", region: "SIEE" }, { id: 32, label: "gbfghfghfghg", status: "EXPIRE", region: "SIAE" } ];
const result = fn(data2);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>

Related

How to improve time complexity of this snippet?

I am trying to improve the time complexity and quality of the code snippet below.
I am iterating through one array to check if the element this array exists in the object, should this be true it should return the name matching the element id in the object.
how can I do this without having a nested loop?
Can someone tell me what I can do to make this algo better, please?
Thank you all in advance.
let genres = [28, 12, 878];
data = {
genres: [
{
id: 28,
name: 'Action',
},
{
id: 12,
name: 'Adventure',
},
{
id: 16,
name: 'Animation',
},
{
id: 35,
name: 'Comedy',
},
{
id: 80,
name: 'Crime',
},
{
id: 99,
name: 'Documentary',
},
{
id: 18,
name: 'Drama',
},
{
id: 10751,
name: 'Family',
},
{
id: 14,
name: 'Fantasy',
},
{
id: 36,
name: 'History',
},
{
id: 27,
name: 'Horror',
},
{
id: 10402,
name: 'Music',
},
{
id: 9648,
name: 'Mystery',
},
{
id: 10749,
name: 'Romance',
},
{
id: 878,
name: 'Science Fiction',
},
{
id: 10770,
name: 'TV Movie',
},
{
id: 53,
name: 'Thriller',
},
{
id: 10752,
name: 'War',
},
{
id: 37,
name: 'Western',
},
],
};
const getGenreName = () => {
let result = [];
for (let genre of data.genres) {
//console.log("genre", genre.name)
for (let id of genres) {
//console.log('id',genres[i])
if (id === genre.id) result.push(genre.name);
}
}
console.log(result);
};
getGenreName();
You can use reduce and includes as others have already shown. This will make the code a bit cleaner, but not change the overall runtime complexity. To improve runtime complexity you may need to use a different data structure.
For instance instead of
let genres = [1,2,3,4];
as a simple array, you could use a Set, which has a better lookup performance.
let genres = new Set([1,2,3,4]);
Then you can use this as follows
let result = data.genres
.filter(g => genres.has(g.id))
.map(g => g.name);
and won't need any explict for loops
The simplest improvement would probably be converting genres to a Set https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
and use the has method to check if each id in the data is a member of the set of chosen genres.
You can also convert the data to a map with the ids as the keys in order to look up by id quickly instead of looping, but that is only faster if the data is reused many times.
JavaScript #reduce in the example outlined below would have O(n) time complexity. This only loops through the array once. We could use filter, and map but it would result in us having to loop through the array twice.
const getGenreName = () => {
const genreSet = new Set(genres);
return data.genres.reduce((accumulator, { id, name }) => {
if (genreSet.has(id)) accumulator.push(name);
return accumulator;
}, []);
};
console.log(getGenreName()); // [ 'Action', 'Adventure', 'Science Fiction' ]
We are initializing the reducer to start with the array [], or an empty array, and then checking to see if the genre property of the object is included in the genres array, if it isn't, return the accumulator, if it is, append it to the end of the accumulator and return it.
You wanted this in one loop, so here it is:
let result = [];
data.genres.forEach(function (e) {
if (genres.includes(e.id)) result.push(e.name);
});
console.log(result);
In case you were wondering about forEach, here's a very good reference: https://www.w3schools.com/jsref/jsref_foreach.asp
The current time complexity is O(MN) where M is the length of data.genres and N is the length of genres.
Time complexity in JavaScript depends on which engine you use, but in most cases you can use a Map to reduce this time complexity to O(max{N,M}):
const getGenreName = () => {
const dataGenresMap = new Map( // O(M)
data.genres.map(({id,...params}) => [id,params]) // O(M)
)
let result = []
for (let id of genres) { // O(N)
if (dataGenresMap.has(id)) result.push(dataGenresMap.get(id).name) // O(1)
}
console.log(result)
}
If you might be doing this more than once then I'd recommend using a Map. By creating a hash map, retrieving genre names per id is much more performant.
let genres = [28, 12, 878];
data = {
genres: [
{
id: 28,
name: 'Action',
},
{
id: 12,
name: 'Adventure',
},
{
id: 16,
name: 'Animation',
},
{
id: 35,
name: 'Comedy',
},
{
id: 80,
name: 'Crime',
},
{
id: 99,
name: 'Documentary',
},
{
id: 18,
name: 'Drama',
},
{
id: 10751,
name: 'Family',
},
{
id: 14,
name: 'Fantasy',
},
{
id: 36,
name: 'History',
},
{
id: 27,
name: 'Horror',
},
{
id: 10402,
name: 'Music',
},
{
id: 9648,
name: 'Mystery',
},
{
id: 10749,
name: 'Romance',
},
{
id: 878,
name: 'Science Fiction',
},
{
id: 10770,
name: 'TV Movie',
},
{
id: 53,
name: 'Thriller',
},
{
id: 10752,
name: 'War',
},
{
id: 37,
name: 'Western',
},
],
};
const genreById = new Map ();
data.genres.forEach(({id, name}) => genreById.set(id, name));
const pushMapValueIfTruthy = map => array => key => {
const val = map.get(key);
if (val) {
array.push(val);
}
};
/** function that takes an array, then id, and pushes corresponding name (if exists) into the array. */
const pushGenreNaneIfExists = pushMapValueIfTruthy(genreById);
const getGenreNames = (ids) => {
result = [];
ids.forEach(pushGenreNaneIfExists(result));
return result;
};
console.log(getGenreNames(genres));

Destructuring object properties from an array of objects and rendering those properties into an array

I currently have two objects (it's a lot but this is what I want to draw your attention to.) As you can see, this is an array of objects. What I want to do is to pull out specified properties of each object and store that in an array. So for example I would want object property Price, I'd like to get each Price property of the object and store all of it in an array that would only contain it's value like in this case : [215.23,215.23]
[{
CompanyName: "Microsoft Corp.",
Date: 1606503905,
Price: 215.23,
Quantity: 50,
Symbol: "MSFT",
TotalCost: 10761.5
},
{
CompanyName: "Microsoft Corp.",
Date: 1606503913,
Price: 215.23,
Quantity: 25,
Symbol: "MSFT",
TotalCost: 5380.75
}
]
Here's a snippet:
function RadialChart(props) {
const { transactionData } = props; //Array of objects stored here
transactionData.map((data) => {console.log(data.TotalCost)})
I tried using useState and the map method but I don't think I was doing it right. When I was using map, I declared a const arr= [] then did an arr.concat(data.TotalCost) and that didn't work. Please let me know if you guys have a solution. Thank you.
If you want just an array of prices, then just map it and return the price:
const prices = transactionData.map((data) => data.Price);
console.log(prices); // [215.23, 215.23]
You can get what you want simply with map.
const myArray = [{
CompanyName: "Microsoft Corp.",
Date: 1606503905,
Price: 215.23,
Quantity: 50,
Symbol: "MSFT",
TotalCost: 10761.5,
},
{
CompanyName: "Microsoft Corp.",
Date: 1606503913,
Price: 215.23,
Quantity: 25,
Symbol: "MSFT",
TotalCost: 5380.75,
}
]
console.log('CompanyName', myArray.map(x => x.CompanyName))
console.log('Date', myArray.map(x => x.Date))
console.log('Price', myArray.map(x => x.Price))
console.log('TotalCost', myArray.map(x => x.TotalCost))
If you want to set it to state...
declare useState
const [ price, setPrice ] = useState([]);
setPrice
setPrice(myArray.map( x => x.Price ))
Use destructuring and map to get the prices. (Just adding destructuring to #Aplet123's suggestion)
const data = [
{
CompanyName: "Microsoft Corp.",
Date: 1606503905,
Price: 215.23,
Quantity: 50,
Symbol: "MSFT",
TotalCost: 10761.5,
},
{
CompanyName: "Microsoft Corp.",
Date: 1606503913,
Price: 123.23,
Quantity: 25,
Symbol: "MSFT",
TotalCost: 5380.75,
},
];
const prices = data.map(({ Price }) => Price);
console.log(prices);

Keep all the keys of the object after removing duplicates from a slightly nested array

I have the same issue of this question but my objects have more keys for example:
[{
id: 1
name: "abcd",
value: 123,
type: "foo"
},
{
id: 1
name: "abcd",
value: 321,
type: "faa"
},
{
id: 2
name: "dcba",
value: 456,
type: "baa"
}]
I want to achieve something like this:
[{
id: 1,
name: "abcd",
value: [123, 321],
type: ["foo", "faa"]
},
{
id: 2
name: "dcba",
value: [456],
type: ["baa"]
}]
The extra keys have the same value.
The idea is to group by the id, then map each group of objects, pick the id and name from the 1st object, extract all value and type from all objects in the group, transpose, and zip to an another object, and merge them.
const { pipe, groupBy, prop, values, map, converge, merge, head, pick, props, transpose, zipObj } = R
const fn = pipe(
groupBy(prop('id')), // groupBy the id
values, // convert the object of groups to array of groups
map(converge(merge, [ // map each group by merging the results of...
pipe(head, pick(['id', 'name'])), // getting the id and name from the 1st item
pipe(map(props(['value', 'type'])), transpose, zipObj(['value', 'type'])) // extract the value and type and zipping to an object
]))
)
const data = [{
id: 1,
name: "abcd",
value: 123,
type: "foo"
},
{
id: 1,
name: "abcd",
value: 321,
type: "faa"
},
{
id: 2,
name: "dcba",
value: 456,
type: "baa"
}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
You can grab the distinct id , loop over them and group them using filter and map
let data = [{
id: 1,
name: "abcd",
value: 123,
type: "foo"
},
{
id: 1,
name: "abcd",
value: 321,
type: "faa"
},
{
id: 2,
name: "dcba",
value: 456,
type: "baa"
}];
//grab unique
let distinct = [...new Set(data.map(a => a.id))];
let grouped = distinct.map(d => {
let filtered=data.filter(d1 => d1.id === d);
return {
id: d,
name: filtered.map(d2 => d2.name)[0],
value: [...new Set(filtered.map(d2 => d2.value))],
type: [...new Set(filtered.map(d2 => d2.type))]
}
});
console.log(grouped);

How to remove some elements in an object array using Lodash

I'll just ask on how to remove some elements in an object array using lodash.
var fruits = [
{ id: 1, name: 'Apple', price: 55, qty: 3, status: 'ripe' },
{ id: 2, name: 'Banana', price: 55, qty: 4, status: 'ripe' },
{ id: 3, name: 'Pineaple', price: 55, qty: 2, status: 'ripe' }
];
How will I remove the qty and status in all object array so it will look like this
[
{ id: 1, name: 'Apple', price: 55 },
{ id: 2, name: 'Banana', price: 55 },
{ id: 3, name: 'Pineaple', price: 55 }
]
Without any library, you can use map and destructure the object.
var fruits = [{"id":1,"name":"Apple","price":55,"qty":3,"status":"ripe"},{"id":2,"name":"Banana","price":55,"qty":4,"status":"ripe"},{"id":3,"name":"Pineaple","price":55,"qty":2,"status":"ripe"}]
var result = fruits.map(({qty,status,...r}) => r);
console.log(result);
You can do it without using a library too.
Use Array.map
var fruits = [{ id: 1, name: 'Apple', price: 55, qty: 3, status: 'ripe' },{ id: 2, name: 'Banana', price: 55, qty: 4, status: 'ripe' },{ id: 3, name: 'Pineaple', price: 55, qty: 2, status: 'ripe' }];
let result = fruits.map(({status,qty,...rest}) => rest);
console.log(result);
You can just iterate through the object using forEach and delete
the unwanted fields with the plain old delete operator.
This method cleans your current object, Without the need for a new object to be defined.
fruits.forEach((val) => {delete val.qty; delete val.status})
It is true that you don't need to use a library to effectively delete properties of an object though if you want to use lodash here is an example of how you would do that by using the .pick method:
let pickedFruits = [];
for (let i in fruits) {
pickedFruits.push(_.pick(fruits[i], ["id", "name", "price"]));
}
where pickedFruits will be the new array of objects each having an id, name and a price property.

Join Two Arrays on a Matching Object Property

I have two arrays that I would like to join
const users = [{ user_id: 100, name: 'Bob' }, { user_id: 101, name: 'Joe' }]
const departments [{ id: 900, manager: 100 }, { id: 901, manager: 101 }]
I want to create a new departments array that contains the user's name by matching the user_id property to the department's manager property.
Is there a simple way to achieve this in lodash (or plain Javascript) ?
The new array would look like this
[{ id: 900, manager: 100, name: 'Bob' }, { id: 900, manager: 101, name: 'Joe' }];
Any help is appreciated!
You can use a Map for faster lookup, and then make the mapping:
const users = [{ user_id: 100, name: 'Bob' }, { user_id: 101, name: 'Joe' }],
departments = [{ id: 900, manager: 100 }, { id: 901, manager: 101 }];
const names = new Map(users.map( user => [user.user_id, user.name] )),
res = departments.map( dep => Object.assign({ name: names.get(dep.manager) }, dep) );
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note that the extra step of creating a Map will lead to O(n) time efficiency as opposed to O(n²) when using an array searching method in each iteration. This is not relevant for small user arrays, but will be when working with larger array sizes.
You can easily do it with just JavaScript using map and find:
const users = [{ user_id: 100, name: 'Bob' }, { user_id: 101, name: 'Joe' }]
const departments = [{ id: 900, manager: 100 }, { id: 901, manager: 101 }]
const result = departments
.map(d => ({
...d,
name: users.find(u =>
u.user_id === d.manager).name
}));
console.log(result);

Categories