map object.keys into string from array of objects - javascript

I have an array, this for example:
interface: [
{ _id: '1', name: 'Foo' },
{ _id: '2', name: 'bar },
{ _id: '3', name: 'boo}
]
i'd now like to map this to a single string ->
this.dataSource.data = data.data.map(item => ({
interface: item.interfaces.name,
}));
so that the result inside the dataSource.data.interface looks like 'Foo, bar, boo'

You could map the name property and join the array.
var data = { interface: [{ _id: '1', name: 'Foo' }, { _id: '2', name: 'bar' }, { _id: '3', name: 'boo' }]};
data.interface = data.interface.map(({ name }) => name).join(', ');
console.log(data);

Use Array.prototype.reduce:
const concatString = data.interface.reduce((acc, value, index, arr) => {
const str = index !== arr.length - 1 ? `${value.name}, ` : value.name;
return acc += str;
}, "");

Related

How group and count elements by lodash?

My data:
[{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}, {
id: 1,
name: 'foo'
}
];
I want to do something like this:
[
{
name: "foo",
id: 1,
count: 2
},
{
name: "bar",
id: 2,
count: 1
}
]
Now i'm grouping the elements with groupBy by name.
Thanks for the help!
var source = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}, {
id: 1,
name: 'foo'
}
];
var result = _(source)
.groupBy('name')
.map(function(items, name) {
return {
name: name,
count: items.length
}
}).value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
It's not exactly what you asked for, but here is a simple method without using lodash. Here I'm using the ID of each element, but you can use anything that uniquely identifies it.
const objArray = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}, {
id: 1,
name: 'foo'
}
];
const reduceAndCount = (arr) => {
const newMap = {};
arr.forEach(el => {
if (!newMap[el.id]) {
el.count = 1
return newMap[el.id] = el
}
newMap[el.id].count = newMap[el.id].count + 1
})
return Object.values(newMap)
}
const result = reduceAndCount(objArray)
console.log(result)
With deference to the answer provided here which uses 'lodash', as requested in the above question, the below points are observed:
the 'groupBy' is fixed (ie, the 'name' field/column/prop) is used
the result given includes the name & count, but the expected result needs the 'id' as well.
EDIT:
Adding solution using lodash,
const arrayRaw = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}, {
id: 1,
name: 'foo'
}];
const groupBy = (col = 'name', arr = arrayRaw) => (
_(arr).groupBy(col).map(it => ({
...it[0],
count: it.length
})).value()
);
console.log(groupBy());
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
In case, someone requires a solution without 'lodash', the below should be one possible implementation:
const arrayRaw = [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}, {
id: 1,
name: 'foo'
}];
const groupBy = (col = 'name', arr = arrayRaw) => (
Object.entries(arr.reduce((fin, itm, idx) => ({
...fin,
[itm[col]]: (fin[itm[col]] || []).concat([idx])
}), {})).map(([k, v]) => ({
...arr[v[0]],
count: v.length
}))
);
console.log(groupBy());
Approach / Explanation
It is self-explanatory, however, should anyone require one - please comment and I shall add.

return the value of the matching items in an array

I have two arrays that I would like to compare and return a respective value of the ones that match.
Taking the 'id' variable as a reference, I want to return all the matching values of fastFood, but only the 'name'.
My expected result is to return Five Guys and KFC, but instead, it returns the entire object.
let id = ['1234'];
let fastFood = [
{_id:'4391', name: "McDonalds"},
{_id:'7654', name: "Burger King"},
{_id:'8765', name: "Dominos"},
{_id:'1234', name: "Five Guys"},
{_id:'9876', name: "Subway"},
{_id:'1234', name: "KFC"}
];
const findFastFood = ids.filter((item) => {
if (item._id.indexOf(id) !== -1) {
return item.name;
}
});
console.log(findFastFood);
Does this help?
let id = ['1234'];
let fastFood = [{
_id: '4391',
name: "McDonalds"
},
{
_id: '7654',
name: "Burger King"
},
{
_id: '8765',
name: "Dominos"
},
{
_id: '1234',
name: "Five Guys"
},
{
_id: '9876',
name: "Subway"
},
{
_id: '1234',
name: "KFC"
}
];
const findFastFood = fastFood.filter((item) => {
if (id.indexOf(item._id) !== -1) {
return item.name
}
}).map(obj => obj.name);
console.log(findFastFood);

Why async.map returns multiple copies of array?

const async = require('async');
const arr = [
{ name: 'john', id: '1' },
{ name: 'Andrie', id: '2' }]
let collectArr = [];
let data = async.mapLimit(arr, 5, async function (input) {
collectArr.push({ name: input.name, id: input.id });
return collectArr;
})
data.then((result) =>{
console.log('success',result);
}).catch(e => console.log('err'));
So here i am providing array to async.mapLimit without callback and expecting promise here.
Expected Output :- [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ,
Got Result :-
[ [ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ],
[ { name: 'john', id: '1' }, { name: 'Andrie', id: '2' } ] ]
So my question is why it is creating multiple copies of array, how to deal with this?
You are needlessly returning a sub array, and the same array reference each iteration, when all you want is to return the new object.
let data = async.mapLimit(arr, 5, async function (input) {
return { name: input.name, id: input.id };
});
Not sure why you need this to be async

Filtering array of objects if specific key contains search term

I am trying to filter through an object with multiple key/value pairs by a specific key. It appears that the code I've written is searching the entire object regardless of the key...
If key name contains the search term, return the search term.
Array of Objects:
export const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
]
Search:
const searchResults = someArrayOfObjects.filter((o) =>
Object.keys(o).some((k) => o[k].toString().toLowerCase().includes(searchTerm.toLowerCase()))
);
So if I search "Something", I only want it to loop through name to search for that term...
You don't need the Object.keys loop.
const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
];
let key = 'name';
let searchTerm = 'th';
const res = someArrayOfObjects.filter(o =>
o[key].toLowerCase().includes(searchTerm.toLowerCase()));
console.log(res);
similar to iota's, you don't need to create the extra array with Object.keys.
just loop/check every item inside the original array with the 'name' key.
you can also try to make it more reusable like below.
const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
];
const search = function (anyArray, searchTerm) {
return anyArray.filter((obj) => {
if (obj.name === searchTerm) {
return obj.name;
}
return false;
});
};
const case1 = search(someArrayOfObjects, 'Something');
console.log(case1);

How do I correctly spread 2 arrays overwriting the old values that matches the object id

My goal is to combine the values in both arrays, overwriting the objects that matches all the _id. Is this possible?
oldState = [
{ _id: 1, label: 'a' },
{ _id: 2, label: 'b' },
{ _id: 3, label: 'c' }
]
newState = [
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
]
Expected Result:
[
{ _id: 1, label: 'a' },
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
]
Doing this:
[...oldState, ...newState]
Results to this:
[
{ _id: 1, label: 'a' },
{ _id: 2, label: 'b' },
{ _id: 3, label: 'c' },
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
]
You must note that spread syntax cannot in itself overrider objects with specific key, you need to do that yourself.
You can make use of Array.prototype.reduce and group the object based on _id and overwrite objects with the same id.
var oldState = [
{ _id: 1, label: 'a' },
{ _id: 2, label: 'b' },
{ _id: 3, label: 'c' }
]
var newState = [
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
]
const res = oldState.concat(newState).reduce((acc, item) => {
acc[item._id] = {...(acc[item._id] || {}), ...item};
return acc;
}, {})
const sol = Object.values(res);
console.log(sol);
I would use Array.reduce() with a Map as the accumulator. The code is a bit cleaner, and the Map also preserves the original order of the items (an object would order them by the numeric value of _id):
const oldState = [{"_id":1,"label":"a"},{"_id":2,"label":"b"},{"_id":3,"label":"c"}]
const newState = [{"_id":2,"label":"updated b"},{"_id":3,"label":"updated c"}]
const result = Array.from(
[...oldState, ...newState]
.reduce((acc, item) => acc.set(item._id, item), new Map())
.values()
)
console.log(result);
I'd reduce all items to an object and then get the object's values:
const oldState = [
{ _id: 1, label: 'a' },
{ _id: 2, label: 'b' },
{ _id: 3, label: 'c' }
];
const newState = [
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
];
const merge = (oldState, newState) => Object.values(
[...oldState, ...newState].reduce((a, v) => (a[v._id] = v, a), {})
);
console.log(merge(oldState, newState));
You can create a Map from your newState with the keys being the object ids and the values being the objects themselves. Then you can map your oldState and replace any old objects with the new objects in from the Map if its id exists:
const oldState = [{ _id: 1, label: 'a' }, { _id: 2, label: 'b' }, { _id: 3, label: 'c' } ];
const newState = [{ _id: 2, label: 'updated b' }, { _id: 3, label: 'updated c' } ];
const newStateMap = new Map(newState.map(o => [o._id, o]));
const res = oldState.map(o => newStateMap.get(o._id) || o);
console.log(res);
You could build objects and spred it into a new object and ge the values from it.
const
getObject = (array, key) => Object.fromEntries(array.map(o => [o[key], o])),
oldState = [{ _id: 1, label: 'a' }, { _id: 2, label: 'b' }, { _id: 3, label: 'c' }],
newState = [{ _id: 2, label: 'updated b' }, { _id: 3, label: 'updated c' }],
result = Object.values({
...getObject(oldState, '_id'),
...getObject(newState, '_id')
});
console.log(result);
It is possible to create an object which will contain unique items and then use map method to create merged object:
const oldState = [
{ _id: 1, label: 'a' },
{ _id: 2, label: 'b' },
{ _id: 3, label: 'c' }
];
const newState = [
{ _id: 2, label: 'updated b' },
{ _id: 3, label: 'updated c' }
];
const mapping = new Map(newState.map(s => [s._id, s]))
const result = oldState.map((s, i)=> ({ ...mapping.get(s._id) || s}));
console.log(result);

Categories

Resources