Say I have an array of 5 objects, each with 2 keys (eg, 'title' & 'author').
I want to check the truthiness that 3 SPECIFIC titles exist in the array.
What's the best way to do that?
I have the following... but it doesn't seem very efficient:
const books = [
{ title: 'Book1', author: 'Author1' },
{ title: 'Book2', author: 'Author2' },
{ title: 'Book3', author: 'Author3' },
{ title: 'Book4', author: 'Author4' },
{ title: 'Book5', author: 'Author5' },
];
const certainBooks = books.some((b) => b.title === 'Book2')
&& books.some((b) => b.title === 'Book3')
&& books.some((b) => b.title === 'Book5')
if (certainBooks) {
// Do stuff
}
If the values and number of titles is dynamic, it might be worth creating an index of titles in the array; something with O(1) time complexity for faster lookups
const books = [
{ title: 'Book1', author: 'Author1' },
{ title: 'Book2', author: 'Author2' },
{ title: 'Book3', author: 'Author3' },
{ title: 'Book4', author: 'Author4' },
{ title: 'Book5', author: 'Author5' },
];
const titleIndex = new Set(books.map(({ title }) => title));
const titlesExist = (...titles) =>
titles.every(title => titleIndex.has(title))
console.log("Book2, Book3, Book5:", titlesExist("Book2", "Book3", "Book5"));
console.log("Book1:", titlesExist("Book1"));
console.log("Book5, Book6:", titlesExist("Book5", "Book6"));
A more general approach would be to map the books to their titles, then check that .every one of the titles you're looking for exists.
const books = [
{ title: 'Book1', author: 'Author1' },
{ title: 'Book2', author: 'Author2' },
{ title: 'Book3', author: 'Author3' },
{ title: 'Book4', author: 'Author4' },
{ title: 'Book5', author: 'Author5' },
];
const titles = books.map(({ title }) => title);
const toFind = ['Book2', 'Book3', 'Book5'];
if (toFind.every(title => titles.includes(title))) {
console.log('do stuff');
}
If the array of books is large, you could benefit by making titles a Set instead of an array - Set#has is faster than Array#includes when there are a lot of elements.
You could loop over them
const books = [
{ title: "Book1", author: "Author1" },
{ title: "Book2", author: "Author2" },
{ title: "Book3", author: "Author3" },
{ title: "Book4", author: "Author4" },
{ title: "Book5", author: "Author5" },
];
const booksNeeded = ["Book2", "Book3", "Book4"];
for (let book of books) {
const lookForIndex = booksNeeded.findIndex(
(title) => title.toLowerCase() === book.title.toLowerCase()
);
if (lookForIndex !== -1) {
booksNeeded.splice(lookForIndex, 1);
}
if (!booksNeeded.length) {
break; // Early break if all the books has been found
}
}
if (!booksNeeded.length) {
console.log("Do Something");
} else {
console.log("Something else");
}
const books = [
{ title: 'Book1', author: 'Author1' },
{ title: 'Book2', author: 'Author2' },
{ title: 'Book3', author: 'Author3' },
{ title: 'Book4', author: 'Author4' },
{ title: 'Book5', author: 'Author5' },
];
let ops = 0;
let search = [ "Book2", "Book3", "Book4" ];
let { length } = search;
for ( let i = 0, len = books.length; length && i < len; i++ ){
ops++;
if ( search.includes(books[i].title) ){
length--;
}
}
if ( !length ){
console.log("All books found!");
} else {
console.log("Not all books found!")
}
console.log( "Number of operations: ", ops );
Related
I'm trying to transform an object contain array to another one with javascript. Below is an example of the object field and what the formatted one should look like.
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
I need The new Fields to looks like this
let newFields = {
name: 'GAME',
tags:[
{ name: 'playPES', value: "{{PES}}" },
{ name: 'playFIFA', value: "{{FIFA}}" }
]},
One contributor suggested me a method like this but i think something need to modify in it but couldn't figure it out.
export const transform = (fields) => ({
tags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
tags: innerFields.map(({code, title: title: {en})=>({name: en, value: code}))
}))
});
// newFields= transform(Fields)
I'm new working with javascript so any help is greatly appreciated, Thanks.
const transform = (o) => {
return Object.entries(o).map((e)=>({
name: e[0],
tags: e[1].map((k)=>({name: (k.title)?k.title.en:undefined, value: k.code}))
}))[0]
}
console.log(transform({
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
}))
Using the entries method you posted:
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
// 1. Obtain keys and values from first object
Fields = Object.entries(oldFields);
// 2. Create new object
const newFields = {};
// 3. Create the name key value pair from new Fields array
newFields.name = Fields[0][0];
// 4. Create the tags key value pair by mapping the subarray in the new Fields array
newFields.tags = Fields[0][1].map(entry => ({ name: entry.title.en, value: entry.code }));
Object.entries(Fields) will return this:
[
"GAME",
[TagsArray]
]
And Object.entries(Fields).map will be mapping this values.
The first map, will receive only GAME, and not an array.
Change the code to something like this:
export const transform = (Fields) => {
const [name, tags] = Object.entries(Fields);
return {
name,
tags: tags.map(({ code, title }) => ({
name: title.en,
value: code
}))
}
}
Hope it help :)
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
let newFields = {
name: 'GAME',
tags:[
{ name: 'playPES', value: "{{PES}}" },
{ name: 'playFIFA', value: "{{FIFA}}" }
]
}
let answer = {
name: "Game",
tags: [
]
}
Fields.GAME.map(i => {
var JSON = {
"name": i.title.en,
"value": i.code
}
answer.tags.push(JSON);
});
console.log(answer);
I think that this is more readable, but not easier... If you want the result as object you need to use reduce, because when you do this
Object.keys(Fields)
Your object transform to array, but reduce can change array to object back.
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
const result = Object.keys(Fields).reduce((acc, rec) => {
return {
name: rec,
tags: Fields[rec].map(el => {
return {
name: el.title.en,
value: el.code
}
})
}
}, {})
console.log(result)
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
const transform = (fields) => ({
tags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
tags: innerFields.map(({code, title: title,en})=>({name: title.en, value: code}))
}))
});
//check required output in console
console.log(transform(Fields));
I have an n levels deep nested array of tag objects with title and ID. What I'm trying to create is a an object with IDs as keys and values being an array describing the title-path to that ID.
I'm no master at recursion so my attempt below doesn't exactly provide the result I need.
Here's the original nested tag array:
const tags = [
{
title: 'Wood',
id: 'dkgkeixn',
tags: [
{
title: 'Material',
id: 'ewyherer'
},
{
title: 'Construction',
id: 'cchtfyjf'
}
]
},
{
title: 'Steel',
id: 'drftgycs',
tags: [
{
title: 'Surface',
id: 'sfkstewc',
tags: [
{
title: 'Polished',
id: 'vbraurff'
},
{
title: 'Coated',
id: 'sdusfgsf'
}
]
},
{
title: 'Quality',
id: 'zsasyewe'
}
]
}
]
The output I'm trying to get is this:
{
'dkgkeixn': ['Wood'],
'ewyherer': ['Wood', 'Material'],
'cchtfyjf': ['Wood', 'Construction'],
'drftgycs': ['Steel'],
'sfkstewc': ['Steel', 'Surface'],
'vbraurff': ['Steel', 'Surface', 'Polished'],
'sdusfgsf': ['Steel', 'Surface', 'Coated'],
'zsasyewe': ['Steel', 'Quality']
}
So I'm building this recursive function which is almost doing it's job, but I keep getting the wrong paths in my flat/key map:
function flatMap(tag, acc, pathBefore) {
if (!acc[tag.id]) acc[tag.id] = [...pathBefore];
acc[tag.id].push(tag.title);
if (tag.tags) {
pathBefore.push(tag.title)
tag.tags.forEach(el => flatMap(el, acc, pathBefore))
}
return acc
}
const keyMap = flatMap({ title: 'Root', id: 'root', tags}, {}, []);
console.log("keyMap", keyMap)
I'm trying to get the path until a tag with no tags and then set that path as value for the ID and then push the items 'own' title. But somehow the paths get messed up.
Check this, makePaths arguments are tags, result object and prefixed titles.
const makePaths = (tags, res = {}, prefix = []) => {
tags.forEach(tag => {
const values = [...prefix, tag.title];
Object.assign(res, { [tag.id]: values });
if (tag.tags) {
makePaths(tag.tags, res, values);
}
});
return res;
};
const tags = [
{
title: "Wood",
id: "dkgkeixn",
tags: [
{
title: "Material",
id: "ewyherer"
},
{
title: "Construction",
id: "cchtfyjf"
}
]
},
{
title: "Steel",
id: "drftgycs",
tags: [
{
title: "Surface",
id: "sfkstewc",
tags: [
{
title: "Polished",
id: "vbraurff"
},
{
title: "Coated",
id: "sdusfgsf"
}
]
},
{
title: "Quality",
id: "zsasyewe"
}
]
}
];
console.log(makePaths(tags));
Task
There is an array of objects of the form:
{
title: 'Macbook Air',
offers: [
{
seller: 'Avic',
price: 1200
},
{
seller: 'Citrus',
price: 1600
}
]
}
It is necessary to Convert to an array of objects of the form:
{
title: 'Macbook Air',
bestOffer: {
name: 'Avic',
price: 1200
}
}
Where bestOffer is an offer with a minimum price value.
Write in one line using map() and find().
Code
"use_strict";
var productList = [
{
title: "Product X1",
offers: [
{
seller: "Company X1",
price: 400
},
{
seller: "Company X2",
price: 200
},
{
seller: "Company X3",
price: 300
}
]
},
{
title: "Product Y1",
offers: [
{
seller: "Company Y1",
price: 1700
},
{
seller: "Company Y2",
price: 1600
},
{
seller: "Company Y3",
price: 1500
},
{
seller: "Company Y4",
price: 1400
}
]
},
{
title: "Product Z1",
offers: [
{
seller: "Company Z1",
price: 50
},
{
seller: "Company Z2",
price: 60
},
{
seller: "Company Z3",
price: 10
},
{
seller: "Company Z4",
price: 90
},
{
seller: "Company Z5",
price: 70
}
]
}
];
const destinations = productList.map(item =>
({
title: item.title,
bestOffer: ({
name: item.offers[0].seller, // TODO: How to implement find() method????
price: item.offers[0].price
})
})
)
console.log(destinations)
My code work for restructuring objects for new array, BUT i dont know how to implement find() method for searching minimal value. Is it possible using find() method in this case??
You can use Array.reduce() to find the best offer by price. On each iteration check if the current item's (o) price is less than the accumulator's (r) price, and take the one with the lowest.
const productList = [{"title":"Product X1","offers":[{"seller":"Company X1","price":400},{"seller":"Company X2","price":200},{"seller":"Company X3","price":300}]},{"title":"Product Y1","offers":[{"seller":"Company Y1","price":1700},{"seller":"Company Y2","price":1600},{"seller":"Company Y3","price":1500},{"seller":"Company Y4","price":1400}]},{"title":"Product Z1","offers":[{"seller":"Company Z1","price":50},{"seller":"Company Z2","price":60},{"seller":"Company Z3","price":10},{"seller":"Company Z4","price":90},{"seller":"Company Z5","price":70}]}]
const findBestOffer = ({ offers = [] }) => offers
.reduce((r, o) => o.price < r.price ? o : r)
const formatOffer = item => item ? ({
name: item.seller,
price: item.price
}) : 'none'
const destinations = productList.map(item => ({
title: item.title,
bestOffer: formatOffer(findBestOffer(item))
}))
console.log(destinations)
If you need to use Array.map() and Array.find() - map the array to the price numbers, and get the lowest one using Math.min(), and then find the item with that price:
const productList = [{"title":"Product X1","offers":[{"seller":"Company X1","price":400},{"seller":"Company X2","price":200},{"seller":"Company X3","price":300}]},{"title":"Product Y1","offers":[{"seller":"Company Y1","price":1700},{"seller":"Company Y2","price":1600},{"seller":"Company Y3","price":1500},{"seller":"Company Y4","price":1400}]},{"title":"Product Z1","offers":[{"seller":"Company Z1","price":50},{"seller":"Company Z2","price":60},{"seller":"Company Z3","price":10},{"seller":"Company Z4","price":90},{"seller":"Company Z5","price":70}]}]
const findBestOffer = ({ offers = [] }) => {
const min = Math.min(...offers.map(o => o.price))
return offers.find(o => o.price === min)
}
const formatOffer = item => item ? ({
name: item.seller,
price: item.price
}) : 'none'
const destinations = productList.map(item => ({
title: item.title,
bestOffer: formatOffer(findBestOffer(item))
}))
console.log(destinations)
Since you prefer to use FIND and MAP, I have a clearer solution for you then.
// product list
const productList = [{"title":"Product X1","offers":[{"seller":"Company X1","price":400},{"seller":"Company X2","price":200},{"seller":"Company X3","price":300}]},{"title":"Product Y1","offers":[{"seller":"Company Y1","price":1700},{"seller":"Company Y2","price":1600},{"seller":"Company Y3","price":1500},{"seller":"Company Y4","price":1400}]},{"title":"Product Z1","offers":[{"seller":"Company Z1","price":50},{"seller":"Company Z2","price":60},{"seller":"Company Z3","price":10},{"seller":"Company Z4","price":90},{"seller":"Company Z5","price":70}]}]
// Search text from Collection
function searchProduct(niddle, haystack){
const searchResult = haystack.find(item=> item['title'] === niddle);
let bestOffer;
if(searchResult){
bestOffer = getBestOffer(searchResult);
}
return bestOffer;
}
// Search for best Offer
function getBestOffer(dataInfo){
let bestOffer = {seller: null,price: null};
dataInfo['offers'].map((item, index)=>{
if(bestOffer.price === null || bestOffer.price > item.price){
bestOffer = item;
}
})
return bestOffer;
}
//run
console.log( searchProduct("Product Z1", productList) );
//output is
// Object {seller: "Company Z3", price: 10}
I hope you like it.
I need to create a Graphql query that outputs data from two arrays of objects. The arrays are:
const authors = [
{
name: 'Robert Martin',
id: 'afa51ab0-344d-11e9-a414-719c6709cf3e',
born: 1952
},
{
name: 'Martin Fowler',
id: 'afa5b6f0-344d-11e9-a414-719c6709cf3e',
born: 1963
},
{
name: 'Fyodor Dostoevsky',
id: 'afa5b6f1-344d-11e9-a414-719c6709cf3e',
born: 1821
},
{
name: 'Joshua Kerievsky', // birthyear not known
id: 'afa5b6f2-344d-11e9-a414-719c6709cf3e'
},
{
name: 'Sandi Metz', // birthyear not known
id: 'afa5b6f3-344d-11e9-a414-719c6709cf3e'
}
];
And:
const books = [
{
title: 'Clean Code',
published: 2008,
author: 'Robert Martin',
id: 'afa5b6f4-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Agile software development',
published: 2002,
author: 'Robert Martin',
id: 'afa5b6f5-344d-11e9-a414-719c6709cf3e',
genres: ['agile', 'patterns', 'design']
},
{
title: 'Refactoring, edition 2',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 3',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 4',
published: 2018,
author: 'Martin Cowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring to patterns',
published: 2008,
author: 'Joshua Kerievsky',
id: 'afa5de01-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'patterns']
},
{
title: 'Practical Object-Oriented Design, An Agile Primer Using
Ruby',
published: 2012,
author: 'Sandi Metz',
id: 'afa5de02-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'design']
},
{
title: 'Crime and punishment',
published: 1866,
author: 'Fyodor Dostoevsky',
id: 'afa5de03-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'crime']
},
{
title: 'The Demon ',
published: 1872,
author: 'Fyodor Dostoevsky',
id: 'afa5de04-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'revolution']
}
];
The desired output format for a query like this:
query {
allAuthors {
name
bookCount
}
}
is like so:
"data": {
"allAuthors": [
{
"name": "Robert Martin",
"bookCount": 2
},
{
"name": "Martin Fowler",
"bookCount": 1
},
{
"name": "Fyodor Dostoevsky",
"bookCount": 2
},
{
"name": "Joshua Kerievsky",
"bookCount": 1
},
{
"name": "Sandi Metz",
"bookCount": 1
}
]
}
I've found a way to count the amount of books for each author and output the data in the desired format (a good example of that here: Summarize count of occurrences in an array of objects with Array#reduce). However this approach ignores other fields in the data, such as "born" and "genres". If I was to expand the query like so:
query {
allAuthors {
name
bookCount
born
}
}
It wouldn't output anything for the field "born". What would be the smart way to create the query resolver? Spread operator? Reduce?
* EDIT *
My unnecessarily complicated solution for counting the books here:
const newBooks = books.reduce((acc, cv) => {
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
arr[0].bookCount += 1;
}
return acc;
}, []);
const array = [];
books.forEach(book => {
const object = {
name: book.author
};
array.push(object);
return array;
});
const unique = array.map(a => a.name);
result = {};
for (var i = 0; i < unique.length; ++i) {
if (!result[unique[i]]) result[unique[i]] = 0;
++result[unique[i]];
}
const entries = Object.entries(result);
const finalAnswer = [];
entries.forEach(entry => {
const object = {
name: entry[0],
bookCount: entry[1]
};
finalAnswer.push(object);
return finalAnswer;
});
console.log(finalAnswer);
You could map the authors and use filter to get the bookCount for each author
const authors=[{name:'Robert Martin',id:'afa51ab0-344d-11e9-a414-719c6709cf3e',born:1952},{name:'Martin Fowler',id:'afa5b6f0-344d-11e9-a414-719c6709cf3e',born:1963},{name:'Fyodor Dostoevsky',id:'afa5b6f1-344d-11e9-a414-719c6709cf3e',born:1821},{name:'Joshua Kerievsky',id:'afa5b6f2-344d-11e9-a414-719c6709cf3e'},{name:'Sandi Metz',id:'afa5b6f3-344d-11e9-a414-719c6709cf3e'}],
books=[{title:'Clean Code',published:2008,author:'Robert Martin',id:'afa5b6f4-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Agile software development',published:2002,author:'Robert Martin',id:'afa5b6f5-344d-11e9-a414-719c6709cf3e',genres:['agile','patterns','design']},{title:'Refactoring, edition 2',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 3',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 4',published:2018,author:'Martin Cowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring to patterns',published:2008,author:'Joshua Kerievsky',id:'afa5de01-344d-11e9-a414-719c6709cf3e',genres:['refactoring','patterns']},{title:'Practical Object-Oriented Design, An Agile Primer Using Ruby ',published:2012,author:'Sandi Metz',id:'afa5de02-344d-11e9-a414-719c6709cf3e',genres:['refactoring','design']},{title:'Crime and punishment',published:1866,author:'Fyodor Dostoevsky',id:'afa5de03-344d-11e9-a414-719c6709cf3e',genres:['classic','crime']},{title:'The Demon ',published:1872,author:'Fyodor Dostoevsky',id:'afa5de04-344d-11e9-a414-719c6709cf3e',genres:['classic','revolution']}];
const output = authors.map(({ born, name }) => {
const bookCount = books.filter(b => b.author === name).length;
return { name, born, bookCount }
})
console.log(output)
I think you can add a statement to your reducer function to add the desired fields. I added the single line, and annotated the rest of the method so you can see what's going on:
const newBooks = books.reduce((acc, cv) => {
// acc is an "accumulation" of the results so far.
// cv is the next item that hasn't been processed.
// Search for author in "accumulator" array acc. Put results in arr.
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
// Haven't seen this author, yet. Add new item to "accumulator" array.
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
// This author already exists in "accumulator" array, referenced by arr[0].
// Update pre-existing item.
arr[0].bookCount += 1;
arr[0].born = cv.born; // <-- This is the new code that is required.
}
return acc;
}, []);
I'm certain this question is very common, but I can't seem to find a robust answer for my use case.
I have an Array of objects with nesting in two levels. Here is an example of the array:
let array = [
{ company: 'CompanyName1',
child: [
{ title: 'title1a',
baby: [
{ title: 'title1ab' },
{ title: 'title1abc' }
]
},
{ title: 'title2a',
baby: [
{ title: 'titleb2abcd' },
{ title: 'titleb2abcde' }
]
}
]
},
{ company: 'CompanyName2',
child: [
{ title: 'title2b',
baby: [
{ title: 'titleb3ab' },
{ title: 'titleb3abc' }
]
}
]
}
]
And this is my expected Array:
let newArray = [
{
company: 'companyName1',
child_title_0: 'title1a',
child_title_1: 'title1a',
child_baby_0: 'title1ab',
child_baby_1: 'title1abc',
child_baby_2: 'title1abcd',
child_baby_3: 'title1abcde',
},
{
company: 'companyName2',
child_title_0: 'title2b',
child_baby_0: 'titleb3ab',
child_baby_1: 'titleb3abc',
}
]
Basically I need to flatten each of the top level objects of the array. Since the nested objects have the same keys (follow a model, and are dynamic -- some items have 10 nested objects, some 0, etc.) I have to dynamically generate each of the new keys, possibly based in the index of the loops.
Any help -- direction is appreciated.
Thanks!
You can use the map function to return a manipulated version of each object in the array.
let results = [
{
company: 'CompanyName1',
child: [
{
title: 'title1a',
baby: [
{ title: 'title1ab' },
{ title: 'title1abc' }
]
},
{
title: 'title2a',
baby: [
{ title: 'titleb2abcd' },
{ title: 'titleb2abcde' }
]
}
]
},
{
company: 'CompanyName2',
child: [
{
title: 'title2b',
baby: [
{ title: 'titleb3ab' },
{ title: 'titleb3abc' }
]
}
]
}
];
let flattened = results.map(company => {
let childCount = 0, babyCount = 0;
company.child.forEach(child => {
company['child_title_'+childCount] = child.title;
child.baby.forEach(baby => {
company['child_baby_'+babyCount] = baby.title;
babyCount++;
});
childCount++;
});
delete company.child;
return company;
});
console.log(flattened);