Javascript - One-liner to extract values from object to array - javascript

I quite simple one:
I have a Javascript object with some properties whose values are arrays, with the following structure:
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"],...}
I need to get an array of arrays with only the values, like the following:
let obj2 = [["xxx#yyy.com"], ["qqq#www.com"], ["asdf"],...]
With Object.values(obj), I get [["xxx#yyy.com", "qqq#www.com"], ["asdf"],...], which is not exactly what I am looking for, but it is a good starting point...
Also, I am looking for a one-liner to do it, if possible. Any ideas?
Thanks.

An alternative using the function reduce.
This approach adds objects and arrays from the first level.
As you can see, this approach evaluates the type of the object.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
var result = Object.values(obj).reduce((a, c) => {
if (Array.isArray(c)) return a.concat(Array.from(c, (r) => [r]));
return a.concat([c]);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One line approach (excluding the checking for array type):
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]},
result = Object.values(obj).reduce((a, c) => (a.concat(Array.from(c, (r) => [r]))), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can use Object.values to get array of values and then concat and spread syntax ... to get flat array and then map method.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
const values = [].concat(...Object.values(obj)).map(e => [e])
console.log(values)

Related

JavaScript object inside Multiple array merge

I have one object inside the object list of array values, I want to merge the object value
Thanks for Help
const List = {
school1: [{studentname:'1'},{studentname:'2'}]
school2: [{studentname:'21'},{studentname:'22'}]
school3: [{studentname:'31'},{studentname:'32'}]
}
Trying Get like this
const List = [{studentname:'1'},{studentname:'2'},{studentname:'21'},{studentname:'22'},{studentname:'31'},{studentname:'32'}]
You can use Object.values() and Array.flat() to get the desired result:
const List = {
school1: [{studentname:'1'},{studentname:'2'}],
school2: [{studentname:'21'},{studentname:'22'}],
school3: [{studentname:'31'},{studentname:'32'}]
}
const result = Object.values(List).flat();
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
var a = {
a1: [{name:'s1'}, {name: 's2'}],
a2: [{name:'s3'}, {name: 's4'}],
a3: [{name:'s5'}, {name: 's6'}],
}
var list = [];
Object.keys(a).forEach(x => {list = [...list, ...a[x]]})
console.log(list)
Brute force solution:
const List = {
school1: [{studentname:'1'},{studentname:'2'}],
school2: [{studentname:'21'},{studentname:'22'}],
school3: [{studentname:'31'},{studentname:'32'}]
}
let List2 = [];
for(let i of Object.values(List)){
List2.push(i)
}
console.log(List2)

Search by multiple keys and values, Javascript

I'm trying to make a search function. The number of filters will change dynamically, a number of keys can be different, and the number of values, too.
My code looks like:
var data = [{"id":"123","color":"Red","model":"Tesla"},{"id":"124","color":"Black","model":"Honda"},{"id":"125","color":"Red","model":"Audi"},{"id":"126","color":"Blue","model":"Resla"}]
var keys = ["color", 'model'];
var values = ["Re"];
var result = data.filter(function(e) {
return keys.every(function(a) {
return values.includes(e[a])
})
})
console.log(result);
Is it possible to search with - startsWith() and not includes()? I guess everything should be in toLowerCase() as well?
Also can I have two separate results as two arrays if results found in one key then it should individual array? So results will be like:
[{ colors: [{"id":"123","color":"Red","model":"Tesla"},{"id":"125","color":"Red","model":"Audi"}], models: [{"id":"126","color":"Blue","model":"Resla" }] }]
Thank you very much in advance.
You need to check keys.some and not keys.every. This will check if the value is part of at least one of the keys and not all of them.
For values, you could create a dynamic regex with alternation and test value against the regex. So, values = ["Re", "Ho"] will create /Re|Ho/
const data = [{"id":"123","color":"Red","model":"Tesla"},{"id":"124","color":"Black","model":"Honda"},{"id":"125","color":"Red","model":"Audi"},{"id":"126","color":"Blue","model":"Resla"}],
keys = ["color", 'model'],
values = ["Ho"],
regex = new RegExp(values.join('|')),
output = data.filter(e => keys.some(k => regex.test(e[k])) )
console.log(output);
If you want to individual results for each key, you can loop through the keys and check for the regex individually.
const data = [{"id":"123","color":"Red","model":"Tesla"},{"id":"124","color":"Black","model":"Honda"},{"id":"125","color":"Red","model":"Audi"},{"id":"126","color":"Blue","model":"Resla"}],
keys = ["color", 'model'],
values = ["Ho", "Re"],
regex = new RegExp(values.join('|')),
group = {}
for (const o of data) {
for (const k of keys) {
if (regex.test(o[k])) {
group[k] ||= []
group[k].push(o)
}
}
}
console.log(group);
You can iterate through each key and value and look it up in object array using array#reduce
const data = [{"id":"123","color":"Red","model":"Tesla"},{"id":"124","color":"Black","model":"Honda"},{"id":"125","color":"Red","model":"Audi"},{"id":"126","color":"Blue","model":"Resla"}],
keys = ["color", 'model'],
values = ["Re"],
initial = Object.assign(...keys.map(k => ({[`${k}s`]: [] }))),
result = data.reduce((r, o) => {
keys.forEach(k => {
values.forEach(val => {
if(o[k] && o[k].startsWith(val)) {
r[`${k}s`].push(o);
}
});
});
return r;
},initial);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Jquery check if one array contains same value

I have an simple javascript array that may contain duplicates or maynot contain duplicates.
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
I need to find the duplicate filepath and put their name into new array. If there is no duplicate then assign its name in first and then assign '' after two values. I could point all the codes that I have tried but it doesnt work. I accept jquery solution also. The expected outcome is this.
var outcome = [
[['aaa','sss','ppp'], 'pin/test1.html'],
[['bbb','eee','xxx'], 'pin/test2.html'],
[['ttt','ggg',''], 'test.html'],
[['yyy','',''], 'un/777.html'],
[['ggg','nnn',''], 'test3.html'],
];
What I have tried is this
for (var i = 0; i < arr.length; i++) {
var uniqueNames = [];
$.each(arr[i], function (i, el) {
if ($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
console.log(uniqueNames);
}
You could take a hash table and an array of empty strings and find the next slot for the value.
The array is reduced by taking an object as accumulator and a destructure array as value (the first part of the array) and key (the second part, aka filepath).
Inside of Array#reduce, a property check with the key is made and if undefined, an array with the wanted structure (array with two items, the first is an array with three emty spaces and the key) is being assigned by using a logical nullish assignment ??=.
The next line assigns the value to the next free slot, an item with an empty string.
Finally the accumulator is returned.
To get only an array as result, a conversion of the values of the object takes place.
let names = [['aaa','pin/test1.html'], ['bbb','pin/test2.html'], ['ttt','test.html'], ['ggg','test.html'], ['yyy','un/777.html'], ['ggg','test3.html'], ['nnn','test3.html'], ['eee','n/777.html'], ['sss','pin/test1.html'], ['xxx','pin/test2.html'], ['ppp','pin/test1.html']],
grouped = Object.values(names.reduce((r, [v, k]) => {
r[k] ??= [Array(3).fill(''), k];
r[k][0][r[k][0].indexOf('')] = v;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const aux = (names) => {
const hash = {};
let max = 0;
names.forEach(ele => {
if (!hash[ele[1]]) hash[ele[1]] = [];
hash[ele[1]].push(ele[0]);
max = Math.max(hash[ele[1]].length, max);
});
return Object.keys(hash).map(ele => [[...hash[ele], ...Array(max -hash[ele].length).fill("")], ele]);
}
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
console.log(aux(names))
This might help
You do not need jQuery for dealing with regular JS structure, you can achieve what you want with a simple code like this:
var names = [['aaa','pin/test1.html'],['bbb','pin/test2.html'],['ttt','test.html'],['ggg','test.html'],['yyy','un/777.html'],['ggg','test3.html'],['nnn','test3.html'],['eee','n/777.html'],['sss','pin/test1.html'],['xxx','pin/test2.html'],['ppp','pin/test1.html'],];
let lengthToFill = 0;
// collecting all the duplicates into a map
const pathMap = {};
names.forEach(name => {
// just in case if you're not familiar with array destructuring
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [pathName, path] = name;
// make sure we have an array to deal with
// just in case you're not familiar with Nullish coalescing operator (??)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
pathMap[path] = pathMap[path] ?? [];
pathMap[path].push(pathName);
// tracking the max number of elements we're adding into a single entry
lengthToFill = Math.max(lengthToFill, pathMap[path].length);
});
const result = Object.entries(pathMap).map(entry => {
// constructing new array entry based on the data we've collected so far
return [
entry[1].concat(Array(lengthToFill - entry[1].length).fill('')),
entry[0],
];
});
console.log(result);
This solution will work for any number of elements that you'd like to fill the array with ''. It makes sure that the length of final listing is the same for all entries.

How to list of objects into list of arrays in js?

I have an array of data [{a:12,b:20},{a:20,b:123}]
How I can convert this to [[12,20],[20,123]]
You can use Array.map() using Object.Values() as the mapping method:
let input = [{a:12,b:20}, {a:20,b:123}];
let res = input.map(Object.values);
console.log(JSON.stringify(res));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
If you need to rely on order of the keys, then refer to #MarkMeyer answer, it can be more appropriate for your purposes.
It's a pretty one-liner with some destructuring:
let l = [{a:12,b:20},{a:20,b:123}]
let arr = l.map(({a, b}) => ([a, b]))
console.log(arr)
const data = [{a:12,b:20},{a:20,b:123}]
let result = []
data.forEach(d => result.push([d.a,d.b]))
console.log(result)
Extract the keys and loop it with your input variable. i have used map function to loop and get data in array format.
var input = [{a:12,b:20},{a:20,b:123}];
var keys = Object.keys(input[0]);
var output = [];
keys.forEach(function(key){
output.push(input.map((item) => item[key]))
})
console.log(output)

How to transform object into sorted array by Lodash

How to transform {2:'b',3:'c',1:'a'} into [{1:'a'},{2:'b'},{3:'c'}] by lodash?
It's fairly trivial using Object.keys + Array.map, you really don't need lodash:
const obj = {2:'b',3:'c',1:'a'};
const arr = Object.keys(obj).map(key => ({ [key]: obj[key] }))
console.log(arr)
Regarding the lack of a sort function, the above code is exploiting the fact that numerically indexed Object keys are (per the spec) stored sequentially. Check the order for yourself:
console.log({2:'b',3:'c',1:'a'})
Here is the relevant portion of the spec
9.1.12 [[OwnPropertyKeys]] ( )
When the [[OwnPropertyKeys]] internal method of O is called the
following steps are taken:
Let keys be a new empty List.
For each own
property key P of O that is an integer index, in ascending numeric
index order
2a. Add P as the last element of keys.
With upcoming Javascript with Object.entries, you could map a new array with single objects.
var data = {2:'b',3:'c',1:'a'},
result = Object
.entries(data)
.sort((a, b) => a[0] - b[0])
.map(([k, v]) => ({ [k]: v }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With lodash, you could use
_.chain,
_.toPairs,
_.sortBy,
_.map and
_.fromPairs
var data = {2:'b',3:'c',1:'a'},
result = _
.chain(data)
.toPairs(data)
.sortBy([0])
.map(o => _.fromPairs([o]));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Lodash is not really necessary to accomplish what you want, but I'm still adding it anyway and add a sorted function. I've also included the native JavaScript way.
const obj = {b: 3, c: 2, a: 1};
const sortByKeys = object => {
const keys = Object.keys(object)
const sortedKeys = _.sortBy(keys)
return _.map(sortedKeys, key => ({ [key]: object[key]}))
}
// the lodash way, and sorted
console.log(sortByKeys(obj))
// simpler way
const result = Object.keys(obj)
.map(key => ({ [key]: obj[key] }))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Why use lodash? Just use regular Javascript. Solution can be cleaned up a bit but the idea is to loop through your object and push your desired format into a new array. I also throw the sorting in there for convenience, but feel free to re-factor to your liking.
const obj = {2:'b',3:'c',1:'a'}
let newArr = [];
for (var key in obj) {
newArr.push({[key]: obj[key]})
newArr.sort((a, b) => a[key] > b[key])
}
console.log(newArr)

Categories