Get array of keys based on values from another array - javascript

Say I have an array of objects that looks like this
let myArray = [
{item1: true},
{item2: false},
{item3: true},
{item4: false}
]
How would I iterate though this to return a new array of true values that looks like this:
let newArray = ['item1', 'item3']
I found this function but it only returns single items:
function findKey(map, term) {
var found = [];
for(var property in map) {
if(map.hasOwnProperty(property)) {
for(var key in map[property]) {
if(map[property].hasOwnProperty(key) && key === term) {
found.push(property);
}
}
}
}
return found;
}

Assuming myArray always contains objects with only 1 property.
let newArray = myArray
.map(item => Object.entries(item)[0])
.filter(([key, value]) => value)
.map(([key, value]) => key)

You could access the first key of each array item via Object.keys(), and use this to filter items with a true value for that first key, and then complete the process with a call to map() to transform the item to a value based on the same "first key" technique:
let myArray = [
{item1: true},
{item2: false},
{item3: true},
{item4: false}
]
let result = myArray
.filter(item => item[ Object.keys(item)[0] ] === true)
.map(item => Object.keys(item)[0])
console.log(result)

Use the function reduce to build the desired output. The handler of the function reduce will get the keys and check for each value === true.
This approach checks for the whole set of keys within an object. Further, this way you only use one loop.
let myArray = [{item1: true},{item2: false},{item3: true},{item4: false}],
result = myArray.reduce((a, c) => a.concat(Object.keys(c).filter(k => c[k] === true)), []);
console.log(result);

Something much optimized than the accepted answer would look like this:
const arr = [
{ item1: true },
{ item2: false },
{ item3: true },
{ item4: false }
]
const result = [];
const len = arr.length;
for (let i = 0; i < len; ++i) {
const obj = arr[i];
const key = Object.keys(obj)[0];
if(obj[key]) {
result.push(key);
}
}
console.log(result);
There is only one loop over the array, instead of map and filter which ends up looping twice.

Shortest
let newArray = myArray.map( x=>Object.keys(x)[0] ).filter( (k,i)=>myArray[i][k] );
In above solution first we use: map which works as for-loop to get array of keys (using Object.keys) ["item1", "item2", "item3", "item4"]. Then we filter that array by choose only those keys for which original array object has true. e.g myArray[0]["item1"] -> true (we use fact that filter funtion takes array element (k) and its index (i) which is the same for elements in myArray). In map and filter we use arrow functions.

Related

From an array of data items how to group, create and collect other, key specific data?

I have an array of objects that looks like below
var FinalArray = [
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"789"},"name":"hello","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"}];
I am trying to loop through the array and return an array of items group by the key "name" which will hold indexes of the items with same key.
expected result like below:
[
{bene: [0,2]},
{leg: [1,3]},
{hello: [4]}
]
I've put together the below but can't get it to work.
var obj = FinalArray.reduce(function(agg, item, index, f) {
var name = item.name || ""
var index = FinalArray.findIndex(item)
/* var copy = [...item.jArray];
*/ if (!agg[name]) {
agg[name] = []
}
agg[name].push(index)
return agg;
}, {})
fairly new to using reduce and groupby. any help is appreciated. Thanks
You can generate an object of the names with their indexes with a reduce on the original array, just pushing indexes into the array for each name.
If you then want an array of those values (I'm not sure this is a better structure), you can use Object.entries to get the key value pairs and map those into individual objects in an array
var FinalArray = [
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"789"},"name":"hello","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"}];
var obj = FinalArray.reduce((acc, { name }, i) => {
acc[name] = (acc[name] || []).concat([i])
return acc
}, {})
console.log(obj)
objArray = Object.entries(obj).map(([k, v]) => ({ [k] : v }))
console.log(objArray)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can achieve this by just using two JavaScript methods Array.forEach() along with Object.keys().
Live Demo :
// Input array
var FinalArray = [
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"789"},"name":"hello","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"}
];
const resObj = {};
const output = [];
// creating a object with the required key: values.
FinalArray.forEach((obj, index) => {
resObj[obj.name] ? resObj[obj.name].push(index) : resObj[obj.name] = [index];
});
// destructuring the object into an array of objects.
Object.keys(resObj).forEach(key => {
output.push({
[key]: resObj[key]
})
});
// final output
console.log(output);
The OP might try a combination of ...
a reduce based approach which straightforwardly creates and collects an index/map of name based groups where the group key resemble an iterated item's name key, and the group value is an array of same name-value item-indices.
and a mapping of the reduced object's entries.
const finalArray = [
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"789"},"name":"hello","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
];
console.log(
'index/map based result ...',
finalArray
.reduce((groups, { name }, idx) => {
(groups[name] ??= []).push(idx);
return groups;
}, {})
);
console.log(
"OP's expected result ...",
Object
.entries(
finalArray
.reduce((groups, { name }, idx) => {
(groups[name] ??= []).push(idx);
return groups;
}, {})
)
.map(([key, value]) => ({ [ key ]: value }))
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Another approach was to solely stick to reduce, where one then needs to implement a reducer function which achieves everything in a single run and does both ...
keeping track of the (to be) generated groups and the (to be) collected indices
and aggregating the final result of the reduce method's accumulator/collector object which gets passed as the method's 2nd parameter ... its initialValue.
const finalArray = [
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"123"},"name":"bene","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"B"}],"Ref":{"docId":"456"},"name":"leg","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
{"jArray":[{"Cd":"A"}],"Ref":{"docId":"789"},"name":"hello","check1":false,"check2":false,"check3":false,"check4":false,"id":"0001"},
];
const { result } = finalArray
.reduce(({ result = [], groups = {} }, { name }, idx) => {
let group = groups[name];
if (!group) {
group = groups[name] = { [ name ]: [] };
result.push(group);
}
group[name].push(idx)
return { result, groups };
}, { result: [] });
console.log({ result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Modify JavaScript code to use higher order functions

My below code is working fine and gives the correct desired output. But I am trying to use map, filter etc. instead of for loop. Lodash map and filter also works.
var arr = [
{"comp_id":1, desc: 'from comp1', updated: true},
{
"comp_id":2, desc: 'from comp2', updated: false}
];
var complaint_sources = [
{"comp_id":2,"consumer_source":"Hotline In","description_option":"English"},
{"comp_id":1,"consumer_source":"Online","description_option":"Other"},
{"comp_id":1,"consumer_source":"Email","description_option":null},
{"comp_id":2,"consumer_source":"Email","description_option":null}]
for(let i =0 ;i<arr.length;i++) {
let x=[];
for(let j=0;j<complaint_sources.length;j++){
if(arr[i].comp_id === complaint_sources[j].comp_id){
x.push(complaint_sources[j]);
arr[i].comp_src = x;
}
}
}
console.log(arr);
Basically I am looping through arr array and inside that looping through the complaint_sources array and when the comp_id matches I am modifying the arr array and adding a comp_src property to the object of arr array. This comp_src property will be an array of complaint_sources matched by comp_id.
this will work:
var arr = [
{"comp_id":1, desc: 'from comp1', updated: true},
{"comp_id":2, desc: 'from comp2', updated: false}
];
var complaint_sources = [
{"comp_id":2,"consumer_source":"Hotline In","description_option":"English"},
{"comp_id":1,"consumer_source":"Online","description_option":"Other"},
{"comp_id":1,"consumer_source":"Email","description_option":null},
{"comp_id":2,"consumer_source":"Email","description_option":null}
];
const grouped_sources = complaint_sources.reduce((acc, value) => {
(acc[value.comp_id] = acc[value.comp_id] || []).push(value);
return acc;
}, {})
const data = arr.map((comp) => ({
...comp,
comp_src: grouped_sources[comp.comp_id]
}));
console.log(data);

Filter object based on condition

I have an object as
sports={
"cricket":true,
"football":true,
"tennis":false
}
I want to filter all the sports which are true into an array like [cricket,football]
follow above code returns keys of true value in array like [cricket,football]
const sports = {
cricket: true,
football: true,
tennis: false,
};
const result = Object.keys(sports).filter((current) => {
return sports[current];
});
Use a for...in structure:
sports={
cricket:true,
football:true,
tennis:false
}
const result = []
for(const sport in sports) {
if (sports[sport]) result.push(sport)
}
console.log(result)
more info about for...in: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
var sports_arr = Object.keys(sports);
var tmp = [];
for(var i = 0; i < sports_arr.length && sports[sports_arr[i]]; i++)
tmp.push(sports_arr[i]);
You could use Object.entries(), Array.prototype.filter() and Array.prototype.map() like so:
const sports = {
cricket:true,
football:true,
tennis:false,
};
console.log(Object.entries(sports).filter(([key, value]) => value).map(([key, value]) => key));
Object.entries() returns an array with key-value pairs:
[
['cricket', true],
['football', true],
['tennis', false],
]
You can then check the second element on each entry to filer out the ones that are false:
[
['cricket', true],
['football', true],
]
And then use map to keep only the first element:
[
'cricket',
'football',
]
As posted by #David784 in the comments, this is the simplest solution. No need to complicate it any further:
const sports = {
cricket:true,
football:true,
tennis:false,
};
const result = Object.keys(sports).filter(key => sports[key]);
console.log(result);
Merely get an array of the keys (Object.keys(sports)) and then discard the ones for which the value isn't true with a .filter().

Merge two dimensional array into array of objects

I would like to know how would I merge this bidimensional array
let arr[
['Reference', 'Price'],
['232323DD, 15.00]
];
I want to convert this into
[
{name: 'Reference', value: '232323DD'},
{name: 'Price', value: 15.00}
]
I've tried this:
Convert a two dimensional array into an array of objects
but It didn't work for me.
You can use .map():
let [keys, values] = [
['Reference', 'Price'],
['232323DD', 15.00]
];
let result = keys.map((k, i) => ({name: k, value: values[i]}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can map through the first array in that array and use their values as the keys to an object:
let arr = [
['Reference', 'Price'],
['232323DD', '15.00']
];
console.log(
arr[0].map((name, i) => ({name, value:arr[1][i]}))
)
If you are unsure about the size of the two arrays, you should first check whether their lengths are equal, to avoid undefined.
Other solution if you are not familiar with map (I think using map for this example make it a bit hard to read)...
const arr = [
['Reference', 'Price'],
['232323DD', 15.00]
]
const obj = []
arr.forEach(x => obj.push({name: x[0], value: x[1]}))
console.log(obj)
You can use the map function. It will run the callback on each array item, return it in a new array.
// where 'arr' is your original array
const new_arr = arr.map((item) => {
// this is called a 'destructuring assignment'
const [name, value] = item;
// return the values as fields in an object
return {name, value};
});
const arrArr = [['Reference', 'Price'], ['232323DD, 15.00]];
const objArr = [];
for (const item of arrArr) {
objArr.push({name: item[0], value: item[1]});
}
let arr = [
['Reference', 'Price'],
['232323DD', '15.00']
];
let result = arr[0].map((key, i) => ({name: key, value: arr[1] ? arr[1][i] : null}));
console.log(result);
I'll try to break this down:
// 1. create a new arr object:
let convertedArr = [];
// 2. loop over the original array:
for(let i = 0; i < arr.length; i++){
let currentItem = arr[i];
//create a temp object
let obj = {name:currentItem[0], value: name:currentItem[1] };
//push a new object to the array
convertedArr.push(obj);
}

merging objects in javascript

I have 2 objects array as follows:
arr1 = [ { 'v1': 'abcde',
'pv_45': 13018,
'geolocation': '17.340291,76.842807'
}]
arr2 =[{ 'v1':'abcde',
'pv_50': 13010,
geolocation: '17.340291,76.842807'
}]
I want to merge the above 2 array int0 single based on condition that 'v1' and 'geolocation' should be same as follows:
[{'v1':'abcde',
'pv_45': 13018,
'pv_50': 13010,
'geolocation': '17.340291,76.842807'}]
I used _.extend, but its not checking any condition blindly it will merge. Please share your ideas. Thanks in advance.
you can use underscore js union and uniq to do that.
var mergedArray = _.uniq(_.union(c1, c2), false, function(item, key, a){ return item; });
Using pure JavaScript it could be done like this:
var arr1 = [ { 'v1': 'abcde',
'pv_45': 13018,
'geolocation': '17.340291,76.842807'
}],
arr2 =[{ 'v1':'abcde',
'pv_50': 13010,
geolocation: '17.340291,76.842807'
}],
mergeOnV1Geo = function (arr1, arr2) {
var mergeObj = {},
merge = function (item) {
var key = item.v1 + ',' + item.geolocation;
// if this is the first object with this key
// create a new object and copy v1, geolocation info
if (!mergeObj[key]) {
mergeObj[key] = {
v1: item.v1,
geolocation: item.geolocation
};
}
// add other props
Object.keys(item).forEach(function (prop) {
if (!prop.match(/v1|geolocation/)) {
mergeObj[key][prop] = item[prop];
}
});
};
arr1.forEach(merge);
arr2.forEach(merge);
// map back into an array
return Object.keys(mergeObj).map(function (key) {
return mergeObj[key];
});
};
mergeOnV1Geo(arr1, arr2);
You could do the following:
var arr3 = [].concat.apply([], arr1, arr2);
var temp =_.groupBy(arr3, 'geolocation');
var result = Object.keys(_.groupBy(arr3, 'geolocation')).map(function(x) { return _.extend.apply(0, p[x]); })
if you prefer ES-6 arrow functions the result becomes
Object.keys(_.groupBy(arr3, 'geolocation')).map((x) => _.extend.apply(0, p[x]);)
ES6: Using spread operator and reduce.
arr1 = [{ v1: 'abcde',
pv_45: 13018,
geolocation: '17.340291,76.842807'
}]
arr2 =[{ v1:'abcde',
pv_50: 13010,
geolocation: '17.340291,76.842807'
}]
// Keys to be grouped i.e whose values should be equal
groupableKeys = ['v1', 'geolocation'];
// Reducer that creates an Object with Key as the
// groupable key values value1::value2 and Value as
// the list of objects whose v1 and geolocation are same
groupableReducer = (a, b) => {
const uniqKey = groupableKeys.map(key => b[key]).join("::");
a[uniqKey] = [...(a[uniqKey] || []), b];
return a;
}
// Merges two objects using the spread operator
mergableReducer = (a, b) => ({...a, ...b})
// Merge two arrays and start processing
groupableKeyObject = [...arr1, ...arr2].reduce(groupableReducer, {})
output = Object.keys(groupableKeyObject)
.map(key =>
groupableKeyObject[key].reduce(mergableReducer, {})
)
console.log(output);

Categories