Popular New Releases in REST
json-server
fastapi
0.75.2
beego
Release v2.0.2
yapi
修复 沙箱 提权问题
NeteaseCloudMusicApi
v4.5.12
Popular Libraries in REST
by public-apis python
184682 MIT
A collective list of free APIs
by typicode javascript
59172 MIT
Get a full fake REST API with zero coding in less than 30 seconds (seriously)
by iptv-org javascript
49563 Unlicense
Collection of publicly available IPTV channels from all over the world
by tiangolo python
44054 MIT
FastAPI framework, high performance, easy to learn, fast to code, ready for production
by beego go
28039 NOASSERTION
beego is an open-source, high-performance web framework for the Go programming language.
by request javascript
25202 Apache-2.0
🏊🏾 Simplified HTTP request client.
by YMFE javascript
23924 Apache-2.0
YApi 是一个可本地部署的、打通前后端及QA的、可视化的接口管理平台
by Binaryify javascript
23544 MIT
网易云音乐 Node.js API service
by encode python
22954 NOASSERTION
Web APIs for Django. 🎸
Trending New libraries in REST
by amplication typescript
7019 Apache-2.0
Amplication is an open‑source development tool. It helps you develop quality Node.js applications without spending time on repetitive coding tasks.
by trpc typescript
5188 MIT
🧙♀️ End-to-end typesafe APIs made easy
by BrasilAPI javascript
4411 MIT
Vamos transformar o Brasil em uma API?
by pestphp php
4192 MIT
Pest is an amazing and elegant PHP Testing Framework with a focus on simplicity
by Mrs4s go
4085 AGPL-3.0
cqhttp的golang实现,轻量、原生跨平台.
by public-api-lists python
3318 MIT
A collective list of free APIs for use in software and web development 🚀 (Clone of https://github.com/public-apis/public-apis)
by ducaale rust
2772 MIT
Friendly and fast tool for sending HTTP requests
by vitalik python
2769 MIT
💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
by encoredev go
2669 MPL-2.0
Encore is the Go Backend Development Engine helping developers escape the maze of complexity.
Top Authors in REST
1
66 Libraries
5638
2
65 Libraries
36172
3
51 Libraries
5355
4
50 Libraries
482
5
42 Libraries
2749
6
37 Libraries
4002
7
36 Libraries
17415
8
34 Libraries
1688
9
30 Libraries
190
10
28 Libraries
6785
1
66 Libraries
5638
2
65 Libraries
36172
3
51 Libraries
5355
4
50 Libraries
482
5
42 Libraries
2749
6
37 Libraries
4002
7
36 Libraries
17415
8
34 Libraries
1688
9
30 Libraries
190
10
28 Libraries
6785
Trending Kits in REST
Fetching JSON array data from an API using React can be used in various contexts where you need to retrieve and display data from an external API in a React application. Some examples might include the following:
- a dashboard or reporting tool that makes use of a REST API to present data
- An application for data visualization or charting that shows current or past data
- An application for social networking or messaging that shows user data
- a retail or e-commerce website that displays product information or inventory from an API
You can use the fetch function, a built-in function for making HTTP queries, or a library like Axios to complete the request to fetch data from a JSON array from an API in a React application.
This pre-written code snippet will show you how to fetch data from a JSON file easily and consume it in your React project.
Steps-
- Declare a state in React to hold API data
- To retrieve API data and store the result, use a JS function
- On page load, add useEffect to the fetch API
Here's an example of how you might implement this:
Fig 1: Preview of the output that you will get on running this code from your IDE.
Code
In this solution we're using React and Axios library.
Instructions
Follow the steps carefully to get the output easily.
- Install the Node.js and React on your IDE(preferable Visual Studio Code).
- Create React Application using npx create-react-app foldername.
- cd foldername.
- Install npm install axios.
- Open the folder in IDE.
- Copy the code using "copy" button above and paste it in app.js file(remove the earlier code from app.js).
- Import libraries.
- Open the terminal from IDE.
- npm start to run the file.
You can also refer this url 'DEMO' for getting the above output.
I hope you found this useful. I have added the link to dependent libraries, version information in the following sections.
I found this code snippet by searching for 'display data from jsonplaceholder api using react'in kandi. You can try any such use case!
Environment Tested
I tested this solution in the following versions. Be mindful of changes when working with other versions.
- The solution is created in Visual Studio Code 1.73.1.
- The solution is tested on node v18.12.1 and npm v8.19.2.
- React version-18.2.0.
- Axios library version-0.26.1.
Using this solution, we are able to fetch Json array data from API using React with simple steps. This process also facilities an easy way to use, hassle-free method to create a hands-on working version of code which would help us to fetch Json array data from API using React.
Dependent Library
You can also search for any libraries on kandi like 'react' and 'axios'.
Support
- For any support on kandi solution kits, please use the chat
- For further learning resources, visit the Open Weaver Community learning page.
The Facebook API enables developers to create apps that interact with the Facebook platform and access its data, such as user profiles, posts, and comments. By leveraging these libraries, users can improve their Facebook marketing strategies, collect data for research purposes, and engage with their audience more effectively.
These libraries are valuable for developers, marketers, and companies that wish to create and deploy Facebook API apps for various objectives such as social media monitoring, influencer marketing, and audience analysis. Users may access and analyze Facebook data in real-time by employing Python Facebook API modules, which can enhance their marketing tactics and audience engagement.
Here is a list of the 8 Best Python Facebook API Libraries:
Facebook-SDK
- Helps in accessing Facebook data, such as user profiles and posts.
- Useful in automating Facebook interactions, such as posting and commenting.
- Helps in managing Facebook ads and campaigns.
- Useful in building custom Facebook applications.
statsforecast
- Helps in forecasting time series data, such as Facebook metrics.
- Useful in predicting future trends in Facebook data.
- Helps in generating statistical models of Facebook data.
- Useful in building custom Facebook analytics tools.
Fbchat
- Helps in automating Facebook Messenger interactions, such as sending and receiving messages.
- Useful in building custom Facebook chatbots.
- Helps in managing Facebook groups and pages.
- Useful in automating Facebook Messenger marketing campaigns.
Facebook Business SDK
- Helps in accessing Facebook data, such as user profiles and posts.
- Useful in automating Facebook interactions, such as posting and commenting.
- Helps in managing Facebook ads and campaigns.
- Useful in building custom Facebook applications.
Facepy
- Helps in accessing Facebook data, such as user profiles and posts.
- Useful in automating Facebook interactions, such as posting and commenting.
- Helps in managing Facebook ads and campaigns.
- Useful in building custom Facebook applications.
PyFacebook
- Helps in accessing Facebook data, such as user profiles and posts.
- Useful in automating Facebook interactions, such as posting and commenting.
- Helps in managing Facebook ads and campaigns.
- Useful in building custom Facebook applications.
Facepager
- Helps in accessing Facebook data, such as user profiles and posts.
- Useful in automating Facebook interactions, such as posting and commenting.
- Helps in managing Facebook ads and campaigns.
- Useful in building custom Facebook applications.
PySocialWatcher
- Helps in monitoring Facebook user activity, such as posts and comments.
- Useful in tracking changes to Facebook user profiles.
- Helps in collecting Facebook data for research purposes.
- Useful in building custom Facebook monitoring tools.
The Instagram API enables developers to create apps that interact with the Instagram platform and access data such as user profiles, media, and comments. By leveraging these libraries, users can streamline their Instagram marketing strategies, collect data for research purposes, and improve their engagement with their audience.
These libraries benefit developers, marketers, and businesses who wish to create and deploy Instagram API applications for various objectives such as social media monitoring, influencer marketing, and audience analysis. Users can access and analyze Instagram data in real time by employing Python Instagram API modules, which can help improve marketing strategies and interaction with their audience.
Here is the list of 8 Best Python Instagram API Libraries:
Instapy
- Helps in automating Instagram interactions, such as liking and commenting.
- Useful in scheduling Instagram posts and stories.
- Helps in generating statistics about Instagram accounts.
- Useful in building Instagram chatbots.
Instaloader
- Helps in downloading Instagram media and metadata.
- Useful in archiving Instagram content for later use.
- Helps in creating backups of Instagram profiles.
- Useful in collecting Instagram data for research purposes.
python-instagram
- Helps in accessing Instagram data, such as user profiles and media.
- Useful in automating Instagram interactions, such as following and unfollowing users.
- Helps in posting photos and videos to Instagram.
- Useful in managing Instagram ads and campaigns.
InstaLooter
- Helps in downloading Instagram media and metadata.
- Useful in archiving Instagram content for later use.
- Helps in creating backups of Instagram profiles.
- Useful in collecting Instagram data for research purposes.
InstaScrape
- Helps in scraping Instagram data, such as user profiles and media.
- Useful in extracting insights about Instagram audiences.
- Helps in collecting Instagram data for research purposes.
- Useful in building custom Instagram crawlers.
Instabot
- Helps in automating Instagram interactions, such as liking, commenting, and following users.
- Useful in creating custom Instagram bots for specific purposes.
- Helps in building Instagram chatbots.
- Useful in scheduling Instagram posts and stories.
InstaLivecli
- Helps in streaming live videos from Instagram.
- Useful in creating custom Instagram live stream tools.
- Helps in monitoring live events on Instagram.
- Useful in building Instagram live chatbots.
BetterCall
- Helps in automating Instagram interactions with a graphical user interface.
- Useful in scheduling Instagram posts and stories.
- Helps in generating statistics about Instagram accounts.
- Useful in building Instagram chatbots with a GUI interface.
Java API gateway is software that accepts an app's user request, diverts it to other backend services, gathers the necessary info, and supplies it to the application user in a single, combined package. It also provides analytics, threat protection layers, and other application security. Java API gateway is a common entry point for all API calls that come into an app, whether hosted on-premises or in the cloud. It receives remote requests and returns the requested data. Consider the web application of a restaurant. Even though all that information is gathered and delivered from various backend microservices or APIs, a user can enter a single request and easily access the:
- Restaurant's menu,
- Diners' photos
- Reviews
- Payment service, and
- A map to check its location using a laptop or cellphone.
An API gateway receives and processes their request. Developers use microservices in DevOps organizations to build and deploy apps in a fast-paced, iterative manner. API gateways provide three key services between a user and a collection of microservices: request routing, API composition, and protocol translation. In addition to simply servicing requests, an API gateway adds value by making data available in a format appropriate for the requestor's technology. For example, someone using a web browser to request information about a retail store receives far more information than someone using a mobile phone to request and view the same store's data.
APIs are one of the most standard ways for microservices to communicate with one another. As integration and interconnectivity gain importance, APIs are becoming more and more crucial daily for software developers. Furthermore, APIs are used to provision infrastructure in modern cloud development, including the serverless model, and can use Java API gateway to deploy and manage serverless functions.
Several Java API libraries help engineers to reuse code for networking other functionalities of the applications. Some examples of standard libraries are- scalecube-services, service-proxy, kong-java-client, aws-gateway-executor, gateleen, handyman, API-gateway, product-microgateway, shenyu, ship-gate, among others.
Check out the below list to find the best top 10 Java API gateway libraries for your application development:
Here are the top python telegram API libraries that any developer can access to simplify the creation and maintenance of telegram bots and other services.
The messaging service Telegram provides two diverse types of APIs for programmers to use. Using the Bot API, you may quickly make software that uses Telegram messages as an interface. You can create your own uniquely designed Telegram clients using the TDLib and Telegram API. Many uses for the Telegram Bot API are possible, ranging from systems that control notifications to video or image editing. Creating interactive games or providing individualized news updates is an additional potential use for the Telegram Bot API.
The best Python Telegram API libraries are presented below, precisely categorized, and picked based on reviews and features.
python-telegram-bot-
- Asynchronous interface for telegram bot API.
- Featuring high-level classes for easy implementation.
- Optional 3rd party dependencies are available.
Telethon-
- Interacting with telegram becomes easy with this python library.
- It is an MTProto library.
- Can also be used with telegram bots.
pyTelegramBotAPI-
- Extensible python API for Telegram bot.
- Contains message handlers and makes function names customizable.
- It is an HTTP-based interface.
aiogram-
- Written with asyncio and aiohttp.
- The framework is completely asynchronous.
- The client/server framework is concurrent.
pyrogram-
- Interact with the main telegram API through a user account.
- Easy install using pip.
- It's asynchronous but can be used synchronously if needed.
telegramscraper-
- Many accounts are supported at the same time.
- Telegram scrapper and adder.
- Scrapping is possible for public groups in bulk.
pytgcalls-
- Exclusive for telegram group calls.
- Uses NodeJS’s WebRTC library.
- Support Pyrogram and Telethon clients.
opentele-
- For switching between tdata and telethon sessions.
- Real data can be used to randomize device info.
- Bot detection can be avoided.
telemirror-
- Client API for telegram message forwarding.
- Message filters to identify incoming messages.
- Mapping of source and target channels.
python-telegram-handler-
- Handles logs using telegram bot API.
- Easy use with registering and obtaining the authentication token.
- If an exception, the handler retrieves chat_id.
aiotg-
- Easy and declarative API.
- Hassle-free setup - no need for SSL certificates or static IP.
- Built-in support for analytics via chatbase.com.
- Automatic handling of Telegram API throttling or timeouts.
Django Calendar Libraries include Automating customer service, marketing, Creating chatbots, customer feedback, and Automating payments.
Python WhatsApp API libraries are libraries of code written in the Python programming language that can develop applications that interact with the WhatsApp messaging platform. These libraries typically enable developers to create tools, bots, and other programs to send and receive messages, process messages and integrate with other services and APIs.
Let us look at these libraries in detail.
yowsup
- Supports both the WhatsApp web and mobile platforms.
- Allows developers to send and receive messages, images, videos, and audio files.
- Supports group chats and broadcast messages.
whatsapp-framework
- Offers a powerful text-parsing feature that allows developers to parse incoming messages.
- Supports both synchronous and asynchronous messaging.
- Easy-to-use design helps developers quickly integrate its features into their applications.
PyWhatKit
- Built-in scheduler allows you to schedule messages to be sent at a later date and time.
- Simple, intuitive, and user-friendly API.
- Possible to read incoming messages and respond to them.
PyWhatsapp
- Offers a built-in event loop, which allows users to process incoming messages and react to them in real time.
- Highly flexible and can be used to create powerful custom WhatsApp bots.
- Supports media sending, which is unavailable in most other Python WhatsApp API libraries.
whatsapp-cli
- Command-line interface (CLI) tool allows users to interact directly with the WhatsApp API from their terminal.
- Many advanced features include group chats, file sharing, broadcast messages, and more.
- Built using the WhatsApp Web API, it is always up-to-date with the latest WhatsApp features.
whatsapp-api-client-python
- An automated setup process that makes it easy to get up and run quickly with the WhatsApp API.
- Supports both Python 2 and Python 3.
- Built-in error handling.
WhatsApp-Chat-Analyzer-API
- Used to generate visualizations of the conversations, such as word clouds, heatmaps, and network graphs.
- Provides a range of features for analyzing conversations, such as sentiment analysis, keyword extraction, and topic modeling.
- Open source and is available for free.
whatsapp-wrapper
- Provides a wrapper for the official WhatsApp API.
- Built-in webhook system.
- Provides robust API endpoints for creating, editing, and deleting conversations.
pywassap
- Offers a complete suite of APIs to interact with WhatsApp services.
- Highly customizable, allowing users to adjust the API to their specific needs.
- Offers a secure and reliable connection with WhatsApp’s servers.
Chat-API
- Automated message scheduling.
- File and media messaging.
- Easy integration with other apps and services.
FAQ
What is WhatsApp Cloud API Python?
WhatsApp API doesn’t directly interact with a “Cloud API” as other services do. It is typically hosted on the business’s servers. It allows them to send and receive messages through WhatsApp.
What is the WhatsApp Business API?
WhatsApp provides a Business API. It allows businesses API to provide a way for businesses to send messages to users. It helps receive messages from users and manage various aspects of business accounts.
WhatsApp Business API supports various programming languages, including Python. It allows developers to integrate WhatsApp messaging capabilities into their applications.
Is there a Python wrapper for WhatsApp?
WhatsApp is restrictive about third-party applications. It helps in interacting with their service to maintain user privacy and security. However, there have been community-driven projects and third-party libraries. It provides Python wrappers or APIs to interact with WhatsApp.
These might have limitations and may not be officially endorsed. However, it is crucial to be cautious when using such tools. It helps ensure compliance with WhatsApp’s terms of service.
What are the different types of WhatsApp messages?
WhatsApp supports various WhatsApp message types. It facilitates different forms of communication. Here are some common types of messages you can send and receive on WhatsApp:
- Text Messages
- Images and Videos
- Location Sharing
- Voice Messages
- Documents
- Stickers and Emojis
- Contacts
- Status Updates
- Reply and Quoted Messages
- GIFs
- Group Messages
When it comes to honeymoon packages, Kerala has earned its reputation as one of the most romantic destinations in the world. Amidst the various jewels that adorn this beautiful state, the Athirapilly Waterfalls stand out as an enchanting and crucial component of Kerala's tourism, especially for honeymooners. Let's delve into the importance of Athirapilly Waterfalls in the tourism map of Kerala honeymoon packages.
Setting the Romantic Tone
Athirapilly Waterfalls, often referred to as the "Niagara Falls of India," are renowned for their breathtaking beauty and serene ambiance. The cascading waters, lush green surroundings, and the soothing sound of the falls create a romantic atmosphere that is unparalleled. For honeymooners, this natural wonder sets the perfect tone for a romantic getaway.
Captivating Backdrop for Memories
The falls provide an awe-inspiring backdrop for capturing those precious honeymoon moments. Newlyweds can have their photographs taken against the backdrop of the falls, creating memories that will last a lifetime. The beauty of Athirapilly Waterfalls enhances the charm of honeymoon photographs, making them even more special.
Secluded Retreats
Several resorts and accommodations are situated in the vicinity of Athirapilly, offering honeymooners secluded and romantic retreats. These resorts often have rooms or cottages with stunning views of the waterfalls, allowing couples to enjoy the beauty and serenity of Athirapilly right from their doorstep.
Adventure and Bonding
Honeymoons are not just about relaxation; they are also about bonding and creating shared experiences. Athirapilly offers adventurous opportunities like trekking and nature walks through the surrounding rainforests. Couples can explore the lush greenery hand in hand, strengthening their bond amidst the wonders of nature.
Monsoon Magic
The monsoon season transforms Athirapilly Waterfalls into a mesmerizing spectacle. The falls are at their fullest during this time, and the entire region comes alive with vibrant greenery. Honeymooners who visit Athirapilly during the monsoons are treated to a romantic experience like no other, with the falls in their full glory and the aroma of wet earth in the air.
Nearby Attractions
Athirapilly's strategic location makes it a gateway to other romantic destinations in Kerala. The nearby Vazhachal Waterfalls, scenic viewpoints, and the Athirapilly-Vazhachal region's natural beauty provide couples with a variety of options for exploration and romantic escapades.
Serenity and Privacy
Unlike some bustling tourist destinations, Athirapilly offers honeymooners a sense of seclusion and privacy. The falls are surrounded by lush forests, and the relatively lower tourist footfall ensures that couples can enjoy peaceful moments together, undisturbed by crowds.
Local Culture and Cuisine
Honeymoon packages in Kerala often include experiences that introduce couples to the local culture and cuisine. Athirapilly allows honeymooners to savor traditional Kerala dishes while enjoying the serene backdrop of the falls. Engaging with the warm and hospitable locals adds a touch of authenticity to the overall experience.
Sustainable Tourism
Athirapilly is also a hub for eco-tourism, emphasizing the importance of responsible and sustainable tourism. Honeymooners who are environmentally conscious can appreciate the efforts to preserve this natural wonder, making their trip not only romantic but also responsible.
In conclusion, Athirapilly Waterfalls plays a pivotal role in the tourism map of Kerala's honeymoon packages. Its romantic allure, captivating natural beauty, and serene ambiance make it a must-visit destination for couples seeking a romantic and memorable getaway. Athirapilly not only provides a scenic backdrop for love but also offers a range of experiences that allow newlyweds to connect, create memories, and embark on a journey of love that begins with the enchantment of these magnificent falls. It's no wonder that Athirapilly Waterfalls has become an essential stop for honeymooners on their journey through Kerala's romantic landscape.
Best Time To Visit Athirapilly waterfalls
The best time to visit Athirapilly Waterfalls is during the post-monsoon and winter months, which generally span from September to February. This period offers the most favorable weather and the most captivating views of the falls. Here's a breakdown of the seasons and their suitability for visiting Athirapilly:
Post-Monsoon (September to November): This is an excellent time to visit Athirapilly as the monsoon rains have receded, leaving the surroundings lush and green. The waterfalls are at their fullest during this time, creating a mesmerizing spectacle. The weather is relatively cool, making it ideal for outdoor activities like trekking and exploring the nearby forests.
Winter (December to February): The winter months are also an excellent time to visit Athirapilly. The weather is pleasantly cool and dry, and the waterfalls maintain a good flow. This is a popular time for tourists, especially honeymooners, as the romantic ambiance is at its peak. Clear skies and comfortable temperatures make it an ideal time for sightseeing and outdoor adventures.
Pre-Monsoon (March to May): While the weather is generally warm during this period, visiting Athirapilly in the pre-monsoon season is still possible. The falls may have slightly lower water levels compared to post-monsoon, but the lush greenery and the surrounding flora are still beautiful. If you can handle the warmth, you can enjoy the serene ambiance with fewer crowds.
Monsoon (June to August): The monsoon season in Kerala, including Athirapilly, is characterized by heavy rainfall. While the falls are at their most spectacular during this time, with a torrential flow of water, it's not the safest or most comfortable time to visit. Landslides and flooding can occur, making it a less suitable period for travel and outdoor activities.
Keep in mind that weather patterns can vary from year to year, so it's a good idea to check the current weather conditions and forecasts before planning your trip. Ultimately, the best time to visit Athirapilly Waterfalls depends on your preferences, whether you prefer the lush greenery and full waterfalls of the post-monsoon and winter months or are willing to experience the dramatic monsoon season, albeit with certain risks and challenges.
In today's globalized world, proficiency in the English language is not just an advantage but often a necessity for individuals seeking higher education opportunities or international career prospects. Likewise, excelling in subjects like physics, chemistry, and mathematics is crucial for academic success. Tuto Training Center UAE, with its commitment to providing top-notch IELTS training and private tutoring services in these subjects, has emerged as a beacon of academic excellence in Dubai and Abu Dhabi.Private Home Tutor In Dubai Abu Dhabi Al Ain
Addressing Your Educational Needs
Tuto Training Center UAE boasts two convenient locations, ensuring that learners across the United Arab Emirates have easy access to their exceptional services. In Dubai, you can find them at M 102, Al Hilal Bank Building, near Al Tawar Centre, Al Qusais-2. Meanwhile, in Abu Dhabi, they are situated at Opal House Building, Al Mamoura Area, Office No. 702. These strategically placed centers make it convenient for students in both cities to benefit from their expertise. Physics Private Tutors In Dubai Abu Dhabi Al Ain
A Phone Call Away
For inquiries, scheduling, or any assistance you may require, you can reach Tuto Training Center UAE via phone at +9715-640-640-55. Their dedicated team is always ready to answer your questions and guide you through the process of enrolling in their courses or arranging private tutoring sessions. Chemistry Private Tutors In Dubai Abu Dhabi Al Ain
Unlocking Your Potential
Tuto Training Center UAE understands the diverse educational needs of students, which is why they offer a wide range of services, including:
Best IELTS Training in Dubai: Mastering the IELTS exam is essential for those planning to study or work abroad. Tuto Training Center UAE's IELTS training program equips students with the language skills needed to excel in the test. Their experienced instructors provide comprehensive guidance, ensuring that students are well-prepared to achieve their desired band scores.
Private Home Tutor in Dubai, Abu Dhabi, Al Ain: For students seeking one-on-one attention and customized learning experiences, Tuto Training Center UAE offers private tutoring services. Their expert tutors specialize in subjects like physics, chemistry, and mathematics, catering to the unique needs of each student. Whether you need assistance with complex mathematical concepts or a deeper understanding of chemical reactions, their tutors are here to help you succeed. Maths Private Tutors In Dubai Abu Dhabi Al Ain
Physics, Chemistry, and Maths Private Tutors in Dubai, Abu Dhabi, Al Ain: The subjects of physics, chemistry, and mathematics can pose challenges to many students. Tuto Training Center UAE's team of dedicated and knowledgeable tutors is well-equipped to simplify these subjects, making them more accessible and enjoyable for learners.
Tuto Training Center UAE is more than just an educational institution; it is a partner in your academic journey. Their commitment to providing exceptional IELTS training and expert private tutoring services in physics, chemistry, and mathematics sets them apart as a trusted source of academic support in Dubai and Abu Dhabi.
If you aspire to excel in the IELTS exam or need expert guidance in the subjects of physics, chemistry, or mathematics, Tuto Training Center UAE, with its highly qualified tutors and convenient locations, is the ideal choice for unlocking your full potential. Contact them today at info@tutotraining.ae to embark on your path to academic success.
Trending Discussions on REST
How to redirect in React Router v6?
Is every "complete" object a "most-derived" object?
Filter out everything before a condition is met, keep all elements after
"Configuring the trigger failed, edit and save the pipeline again" with no noticeable error and no further details
Multiple labels per item on Kendo chart
Python 3.10 pattern matching (PEP 634) - wildcard in string
Redirect in react-router-dom V6
Patch request not patching - 403 returned - django rest framework
Haskell: Can I read integers directly into an array?
Typescript: deep keyof of a nested object, with related type
QUESTION
How to redirect in React Router v6?
Asked 2022-Mar-24 at 17:22I am trying to upgrade to React Router v6 (react-router-dom 6.0.1
).
Here is my updated code:
1import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
2
3<BrowserRouter>
4 <Routes>
5 <Route path="/" element={<Home />} />
6 <Route path="/lab" element={<Lab />} />
7 <Route render={() => <Navigate to="/" />} />
8 </Routes>
9</BrowserRouter>
10
The last Route
is redirecting the rest of paths to /
.
However, I got an error
TS2322: Type '{ render: () => Element; }' is not assignable to type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'. Property 'render' does not exist on type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'.
However, based on the doc, it does have render
for Route
. How to use it correctly?
ANSWER
Answered 2022-Mar-18 at 18:41I think you should use the no match route approach.
Check this in the documentation.
https://reactrouter.com/docs/en/v6/getting-started/tutorial#adding-a-no-match-route
1import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
2
3<BrowserRouter>
4 <Routes>
5 <Route path="/" element={<Home />} />
6 <Route path="/lab" element={<Lab />} />
7 <Route render={() => <Navigate to="/" />} />
8 </Routes>
9</BrowserRouter>
10import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
11
12<BrowserRouter>
13 <Routes>
14 <Route path="/" element={<Home />} />
15 <Route path="/lab" element={<Lab />} />
16 <Route
17 path="*"
18 element={<Navigate to="/" replace />}
19 />
20 </Routes>
21</BrowserRouter>
22
To keep the history clean, you should set replace
prop. This will avoid extra redirects after the user click back. Thanks @Paul for this tip.
QUESTION
Is every "complete" object a "most-derived" object?
Asked 2022-Mar-21 at 02:30Per [intro.object]/2:
[..] An object that is not a subobject of any other object is called a complete object [..].
So consider this snippet of code:
1struct Base {};
2struct Derived : Base {};
3struct MostDerived : Derived {};
4
I can't understand the wording in this quote from the standard:
If a complete object, a member subobject, or an array element is of class type, its type is considered the most derived class [..] An object of a most derived class type or of a non-class type is called a most derived object.
From the quote what I understand is that a type of a complete object is of "most-derived" class type. I stopped here, I really do not understand the rest of the wording.
Per the question "What does the "most derived object" mean?" I think that (correct me if I am wrong), objects of type "most-derived" class only, like
MostDerived
, are called "most-derived" objects. Is this true?If I have created an object of
Base
like this:Base b_obj = Base()
, is the objectb_obj
a "most-derived" object?If I have created an object of
Derived
like this:Derived d_obj = Derived()
, is the objectd_obj
also a "most-derived" object?Does the word "derived" in "most-derived" mean that the object is an object of a class like
MostDerived
, or mean that the object has no class subobject in it?
ANSWER
Answered 2022-Mar-21 at 00:32- An object is not a class.
- An object is an instantiation of a class, an array, or built-in-type.
- Subobjects are class member objects, array elements, or base classes of an object.
- Derived objects (and most-derived objects) only make sense in the context of class inheritance.
1struct Base {};
2struct Derived : Base {};
3struct MostDerived : Derived {};
4void foo() {
5 int i = 0; // complete object, but not most-derived (not class type)
6}
7
8class A {
9 int i = 0; // non complete object, not most-derived
10}
11
12void bar() {
13 A a; // complete object, but not derived, so can't be "most derived"
14}
15
16class B : A { }
17
18void biz() {
19 B b; // complete object, derived object, and most-derived object
20}
21
Is every "complete" object is "most-derived" object
No. A most-derived object is an object of a most-derived class, and a most-derived class must be of a class type. Objects may be of class type, but non-class type objects also exist.
- Every complete object of class-type is a most-derived object only if that class inherits.
- A most-derived object may be a subobject, so you cannot infer object completeness from most-derivedness (however, you can infer that the most-derived object is of class type).
So if I have created an object of Base like this:
Base b_obj = Base()
, Is the objectb_obj
is "most-derived" object?
Yes. The most-derived object of b_obj
is an object of type Base
. This is not necessarily a complete object, however, since this could be a class member definition. Again, complete is not synonymous with most-derived.
Also if I have created an object of Derived like this:
Derived d_obj = Derived()
, Is the objectd_obj
is also a "most-derived" object?
Yes. The most-derived object of d_obj
is an object of type Derived
.
If you have an object created as type MostDerived
:
1struct Base {};
2struct Derived : Base {};
3struct MostDerived : Derived {};
4void foo() {
5 int i = 0; // complete object, but not most-derived (not class type)
6}
7
8class A {
9 int i = 0; // non complete object, not most-derived
10}
11
12void bar() {
13 A a; // complete object, but not derived, so can't be "most derived"
14}
15
16class B : A { }
17
18void biz() {
19 B b; // complete object, derived object, and most-derived object
20}
21MostDerived md;
22
- It is an object of type
MostDerived
- It is an object of type
Derived
- It is an object of type
Base
- If it is not a member subobject, then it is a complete object
- Its most-derived object is of type
MostDerived
- It has a subobject of type
Derived
, which is neither a complete object nor a most-derived object - Its subobject of type
Derived
has a subobject of typeBase
, which is neither a complete object nor a most-derived object.
QUESTION
Filter out everything before a condition is met, keep all elements after
Asked 2022-Feb-23 at 21:32I was wondering if there was an easy solution to the the following problem. The problem here is that I want to keep every element occurring inside this list after the initial condition is true. The condition here being that I want to remove everything before the condition that a value is greater than 18 is true, but keep everything after. Example
Input:
1p = [4,9,10,4,20,13,29,3,39]
2
Expected output:
1p = [4,9,10,4,20,13,29,3,39]
2p = [20,13,29,3,39]
3
I know that you can filter over the entire list through
1p = [4,9,10,4,20,13,29,3,39]
2p = [20,13,29,3,39]
3[x for x in p if x>18]
4
But I want to stop this operation once the first value above 18 is found, and then include the rest of the values regardless if they satisfy the condition or not. It seems like an easy problem but I haven't found the solution to it yet.
ANSWER
Answered 2022-Feb-05 at 19:59You can use itertools.dropwhile
:
1p = [4,9,10,4,20,13,29,3,39]
2p = [20,13,29,3,39]
3[x for x in p if x>18]
4from itertools import dropwhile
5
6p = [4,9,10,4,20,13,29,3,39]
7
8p = dropwhile(lambda x: x <= 18, p)
9print(*p) # 20 13 29 3 39
10
In my opinion, this is arguably the easiest-to-read version. This also corresponds to a common pattern in other functional programming languages, such as dropWhile (<=18) p
in Haskell and p.dropWhile(_ <= 18)
in Scala.
Alternatively, using walrus operator (only available in python 3.8+):
1p = [4,9,10,4,20,13,29,3,39]
2p = [20,13,29,3,39]
3[x for x in p if x>18]
4from itertools import dropwhile
5
6p = [4,9,10,4,20,13,29,3,39]
7
8p = dropwhile(lambda x: x <= 18, p)
9print(*p) # 20 13 29 3 39
10exceeded = False
11p = [x for x in p if (exceeded := exceeded or x > 18)]
12print(p) # [20, 13, 29, 3, 39]
13
But my guess is that some people don't like this style. In that case, one can do an explicit for
loop (ilkkachu's suggestion):
1p = [4,9,10,4,20,13,29,3,39]
2p = [20,13,29,3,39]
3[x for x in p if x>18]
4from itertools import dropwhile
5
6p = [4,9,10,4,20,13,29,3,39]
7
8p = dropwhile(lambda x: x <= 18, p)
9print(*p) # 20 13 29 3 39
10exceeded = False
11p = [x for x in p if (exceeded := exceeded or x > 18)]
12print(p) # [20, 13, 29, 3, 39]
13for i, x in enumerate(p):
14 if x > 18:
15 output = p[i:]
16 break
17else:
18 output = [] # alternatively just put output = [] before for
19
QUESTION
"Configuring the trigger failed, edit and save the pipeline again" with no noticeable error and no further details
Asked 2022-Feb-16 at 10:33I have run in to an odd problem after converting a bunch of my YAML pipelines to use templates for holding job logic as well as for defining my pipeline variables. The pipelines run perfectly fine, however I get a "Some recent issues detected related to pipeline trigger." warning at the top of the pipeline summary page and viewing details only states: "Configuring the trigger failed, edit and save the pipeline again."
The odd part here is that the pipeline works completely fine, including triggers. Nothing is broken and no further details are given about the supposed issue. I currently have YAML triggers overridden for the pipeline, but I did also define the same trigger in the YAML to see if that would help (it did not).
I'm looking for any ideas on what might be causing this or how I might be able to further troubleshoot it given the complete lack of detail that the error/warning provides. It's causing a lot of confusion among developers who think there might be a problem with their builds as a result of the warning.
Here is the main pipeline. the build repository is a shared repository for holding code that is used across multiple repos in the build system. dev.yaml contains dev environment specific variable values. Shared holds conditionally set variables based on the branch the pipeline is running on.
1name: ProductName_$(BranchNameLower)_dev_$(MajorVersion)_$(MinorVersion)_$(BuildVersion)_$(Build.BuildId)
2resources:
3 repositories:
4 - repository: self
5 - repository: build
6 type: git
7 name: Build
8 ref: master
9
10# This trigger isn't used yet, but we want it defined for later.
11trigger:
12 batch: true
13 branches:
14 include:
15 - 'dev'
16
17variables:
18- template: YAML/variables/shared.yaml@build
19- template: YAML/variables/dev.yaml@build
20
21jobs:
22- template: ProductNameDevJob.yaml
23 parameters:
24 pipelinePool: ${{ variables.PipelinePool }}
25 validRef: ${{ variables.ValidRef }}
26
Then this is the start of the actual job yaml. It provides a reusable definition of the job that can be used in more than one over-arching pipeline:
1name: ProductName_$(BranchNameLower)_dev_$(MajorVersion)_$(MinorVersion)_$(BuildVersion)_$(Build.BuildId)
2resources:
3 repositories:
4 - repository: self
5 - repository: build
6 type: git
7 name: Build
8 ref: master
9
10# This trigger isn't used yet, but we want it defined for later.
11trigger:
12 batch: true
13 branches:
14 include:
15 - 'dev'
16
17variables:
18- template: YAML/variables/shared.yaml@build
19- template: YAML/variables/dev.yaml@build
20
21jobs:
22- template: ProductNameDevJob.yaml
23 parameters:
24 pipelinePool: ${{ variables.PipelinePool }}
25 validRef: ${{ variables.ValidRef }}
26parameters:
27- name: dependsOn
28 type: object
29 default: {}
30- name: pipelinePool
31 default: ''
32- name: validRef
33 default: ''
34- name: noCI
35 type: boolean
36 default: false
37- name: updateBeforeRun
38 type: boolean
39 default: false
40
41jobs:
42- job: Build_ProductName
43 displayName: 'Build ProductName'
44 pool:
45 name: ${{ parameters.pipelinePool }}
46 demands:
47 - msbuild
48 - visualstudio
49 dependsOn:
50 - ${{ each dependsOnThis in parameters.dependsOn }}:
51 - ${{ dependsOnThis }}
52 condition: and(succeeded(), eq(variables['Build.SourceBranch'], variables['ValidRef']))
53
54 steps:
55**step logic here
56
Finally, we have the variable YAML which conditionally sets pipeline variables based on what we are building:
1name: ProductName_$(BranchNameLower)_dev_$(MajorVersion)_$(MinorVersion)_$(BuildVersion)_$(Build.BuildId)
2resources:
3 repositories:
4 - repository: self
5 - repository: build
6 type: git
7 name: Build
8 ref: master
9
10# This trigger isn't used yet, but we want it defined for later.
11trigger:
12 batch: true
13 branches:
14 include:
15 - 'dev'
16
17variables:
18- template: YAML/variables/shared.yaml@build
19- template: YAML/variables/dev.yaml@build
20
21jobs:
22- template: ProductNameDevJob.yaml
23 parameters:
24 pipelinePool: ${{ variables.PipelinePool }}
25 validRef: ${{ variables.ValidRef }}
26parameters:
27- name: dependsOn
28 type: object
29 default: {}
30- name: pipelinePool
31 default: ''
32- name: validRef
33 default: ''
34- name: noCI
35 type: boolean
36 default: false
37- name: updateBeforeRun
38 type: boolean
39 default: false
40
41jobs:
42- job: Build_ProductName
43 displayName: 'Build ProductName'
44 pool:
45 name: ${{ parameters.pipelinePool }}
46 demands:
47 - msbuild
48 - visualstudio
49 dependsOn:
50 - ${{ each dependsOnThis in parameters.dependsOn }}:
51 - ${{ dependsOnThis }}
52 condition: and(succeeded(), eq(variables['Build.SourceBranch'], variables['ValidRef']))
53
54 steps:
55**step logic here
56variables:
57- ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/dev'), eq(variables['Build.SourceBranch'], 'refs/heads/users/ahenderson/azure_devops_build')) }}:
58 - name: BranchName
59 value: Dev
60** Continue with rest of pipeline variables and settings of each value for each different context.
61
ANSWER
Answered 2021-Aug-17 at 14:58I think I may have figured out the problem. It appears that this is related to the use of conditionals in the variable setup. While the variables will be set in any valid trigger configuration, it appears that the proper values are not used during validation and that may have been causing the problem. Switching my conditional variables to first set a default value and then replace the value conditionally seems to have fixed the problem.
It would be nice if Microsoft would give a more useful error message here, something to the extent of the values not being found for a given variable, but adding defaults does seem to have fixed the problem.
QUESTION
Multiple labels per item on Kendo chart
Asked 2022-Jan-02 at 21:14I'm trying to get multiple label per item on Kendo Column chart Desired layout looks like this
I was able to get only this layout
1import { Component } from '@angular/core';
2import { groupBy, GroupResult } from '@progress/kendo-data-query';
3import { ValueAxisLabels } from '@progress/kendo-angular-charts';
4
5export type TrendItem = {
6 clientName: string;
7 periodName: string;
8 income: number;
9};
10
11@Component({
12 selector: 'my-app',
13 template: `
14 <kendo-chart>
15 <kendo-chart-category-axis>
16 <kendo-chart-category-axis-item [categories]="categories">
17 </kendo-chart-category-axis-item>
18 </kendo-chart-category-axis>
19
20 <kendo-chart-value-axis>
21 <kendo-chart-value-axis-item [labels]="valueAxisLabels">
22 </kendo-chart-value-axis-item>
23 </kendo-chart-value-axis>
24
25 <kendo-chart-series>
26 <kendo-chart-series-item *ngFor="let groupedResult of groupedTrendsByPeriod" [data]="groupedResult.items" field="income" type="column">
27 <kendo-chart-series-item-labels [content]="labelVisual">
28 </kendo-chart-series-item-labels>
29 </kendo-chart-series-item>
30 </kendo-chart-series>
31 </kendo-chart>
32 `,
33})
34export class AppComponent {
35 public valueAxisLabels: ValueAxisLabels = {
36 font: 'bold 16px Arial, sans-serif',
37 };
38
39 public trendItems: TrendItem[] = [
40 {
41 clientName: 'Client1',
42 periodName: 'Q1 2020',
43 income: 20,
44 },
45 {
46 clientName: 'Client1',
47 periodName: 'Q2 2020',
48 income: 15,
49 },
50 {
51 clientName: 'Client1',
52 periodName: 'Q3 2020',
53 income: 35,
54 },
55 {
56 clientName: 'Client1',
57 periodName: 'Q4 2020',
58 income: 40,
59 },
60 {
61 clientName: 'Client2',
62 periodName: 'Q1 2020',
63 income: 15,
64 },
65 {
66 clientName: 'Client2',
67 periodName: 'Q2 2020',
68 income: 20,
69 },
70 {
71 clientName: 'Client2',
72 periodName: 'Q3 2020',
73 income: 15,
74 },
75 {
76 clientName: 'Client2',
77 periodName: 'Q4 2020',
78 income: 30,
79 }
80 ];
81
82 public categories = (groupBy(this.trendItems, [{ field: 'clientName' }]) as GroupResult[])
83 .map((e) => e.value);
84
85 public groupedTrendsByPeriod = groupBy(this.trendItems, [{ field: 'periodName' }]) as GroupResult[];
86
87 public labelVisual(e: { dataItem: TrendItem }) {
88 return `$${e.dataItem.income}\r\n${e.dataItem.periodName}`;
89 }
90}
91
You can try this code here.
My current result look like this
So my question is how to display multiple labels per item like on the first picture?
My current obstacles.
- I didn't find a way to add multiple
<kendo-chart-series-item-labels>
elements. Only one will be rendered, rest will be ignored. - I didn't find a way to position labels below column chart. For column chart it's only possible to use "center", "insideBase", "insideEnd", "outsideEnd" options (according to API Reference) but none of them gives me desired position.
ANSWER
Answered 2022-Jan-02 at 08:18QUESTION
Python 3.10 pattern matching (PEP 634) - wildcard in string
Asked 2021-Dec-17 at 10:43I got a large list of JSON objects that I want to parse depending on the start of one of the keys, and just wildcard the rest. A lot of the keys are similar, like "matchme-foo"
and "matchme-bar"
. There is a builtin wildcard, but it is only used for whole values, kinda like an else
.
I might be overlooking something but I can't find a solution anywhere in the proposal:
https://docs.python.org/3/whatsnew/3.10.html#pep-634-structural-pattern-matching
Also a bit more about it in PEP-636:
https://www.python.org/dev/peps/pep-0636/#going-to-the-cloud-mappings
My data looks like this:
1data = [{
2 "id" : "matchme-foo",
3 "message": "hallo this is a message",
4 },{
5 "id" : "matchme-bar",
6 "message": "goodbye",
7 },{
8 "id" : "anotherid",
9 "message": "completely diffrent event"
10 }, ...]
11
I want to do something that can match the id without having to make a long list of |
's.
Something like this:
1data = [{
2 "id" : "matchme-foo",
3 "message": "hallo this is a message",
4 },{
5 "id" : "matchme-bar",
6 "message": "goodbye",
7 },{
8 "id" : "anotherid",
9 "message": "completely diffrent event"
10 }, ...]
11for event in data:
12 match event:
13 case {'id':'matchme-*'}: # Match all 'matchme-' no matter what comes next
14 log.INFO(event['message'])
15 case {'id':'anotherid'}:
16 log.ERROR(event['message'])
17
It's a relatively new addition to Python so there aren't many guides on how to use it yet.
ANSWER
Answered 2021-Dec-17 at 10:43You can use a guard:
1data = [{
2 "id" : "matchme-foo",
3 "message": "hallo this is a message",
4 },{
5 "id" : "matchme-bar",
6 "message": "goodbye",
7 },{
8 "id" : "anotherid",
9 "message": "completely diffrent event"
10 }, ...]
11for event in data:
12 match event:
13 case {'id':'matchme-*'}: # Match all 'matchme-' no matter what comes next
14 log.INFO(event['message'])
15 case {'id':'anotherid'}:
16 log.ERROR(event['message'])
17for event in data:
18 match event:
19 case {'id': x} if x.startswith("matchme"): # guard
20 print(event["message"])
21 case {'id':'anotherid'}:
22 print(event["message"])
23
Quoting from the official documentation,
Guard
We can add an
if
clause to a pattern, known as a “guard”. If the guard isfalse
, match goes on to try the nextcase
block. Note that value capture happens before the guard is evaluated:
1data = [{
2 "id" : "matchme-foo",
3 "message": "hallo this is a message",
4 },{
5 "id" : "matchme-bar",
6 "message": "goodbye",
7 },{
8 "id" : "anotherid",
9 "message": "completely diffrent event"
10 }, ...]
11for event in data:
12 match event:
13 case {'id':'matchme-*'}: # Match all 'matchme-' no matter what comes next
14 log.INFO(event['message'])
15 case {'id':'anotherid'}:
16 log.ERROR(event['message'])
17for event in data:
18 match event:
19 case {'id': x} if x.startswith("matchme"): # guard
20 print(event["message"])
21 case {'id':'anotherid'}:
22 print(event["message"])
23match point:
24 case Point(x, y) if x == y:
25 print(f"The point is located on the diagonal Y=X at {x}.")
26 case Point(x, y):
27 print(f"Point is not on the diagonal.")
28
QUESTION
Redirect in react-router-dom V6
Asked 2021-Dec-15 at 05:41I need to navigate back to the original requested URL after login.
For example, user enters www.example.com/settings
as user is not authenticated, it will navigate to login page www.example.com/login
.
Once authenticated, it should navigate back to www.example.com/settings
automatically.
My original approach with react-router-dom
v5 is quite simple:
1const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
2 return (
3 <Route
4 {...rest}
5 render={(props) =>
6 isLoggedIn? (
7 <Component {...props} />
8 ) : (
9 <Redirect
10 to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
11 />
12 )
13 }
14 />
15 );
16};
17
18
19<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />
20
21
Can some one tell me how to do that in v6? Thanks in advance
ANSWER
Answered 2021-Dec-15 at 05:41In react-router-dom
v6 rendering routes and handling redirects is quite different than in v5. Gone are custom route components, they are replaced with a wrapper component pattern.
v5 - Custom Route
Takes props and conditionally renders a Route
component with the route props passed through or a Redirect
component with route state holding the current location
.
1const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
2 return (
3 <Route
4 {...rest}
5 render={(props) =>
6 isLoggedIn? (
7 <Component {...props} />
8 ) : (
9 <Redirect
10 to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
11 />
12 )
13 }
14 />
15 );
16};
17
18
19<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />
20
21const CustomRoute = ({ isLoggedIn, ...props }) => {
22 const location = useLocation();
23 return isLoggedIn? (
24 <Route {...props} />
25 ) : (
26 <Redirect
27 to={{
28 pathname: `/login/${location.search}`,
29 state: { location },
30 }}
31 />
32 );
33};
34
...
1const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
2 return (
3 <Route
4 {...rest}
5 render={(props) =>
6 isLoggedIn? (
7 <Component {...props} />
8 ) : (
9 <Redirect
10 to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
11 />
12 )
13 }
14 />
15 );
16};
17
18
19<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />
20
21const CustomRoute = ({ isLoggedIn, ...props }) => {
22 const location = useLocation();
23 return isLoggedIn? (
24 <Route {...props} />
25 ) : (
26 <Redirect
27 to={{
28 pathname: `/login/${location.search}`,
29 state: { location },
30 }}
31 />
32 );
33};
34<PrivateRoute
35 exact
36 isLoggedIn={isLoggedIn}
37 path="/settings"
38 component={Settings}
39/>
40
v6 - Custom Wrapper
Takes props and conditionally renders an Outlet
component for nested Route
components to be rendered into or a Navigate
component with route state holding the current location
.
1const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
2 return (
3 <Route
4 {...rest}
5 render={(props) =>
6 isLoggedIn? (
7 <Component {...props} />
8 ) : (
9 <Redirect
10 to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
11 />
12 )
13 }
14 />
15 );
16};
17
18
19<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />
20
21const CustomRoute = ({ isLoggedIn, ...props }) => {
22 const location = useLocation();
23 return isLoggedIn? (
24 <Route {...props} />
25 ) : (
26 <Redirect
27 to={{
28 pathname: `/login/${location.search}`,
29 state: { location },
30 }}
31 />
32 );
33};
34<PrivateRoute
35 exact
36 isLoggedIn={isLoggedIn}
37 path="/settings"
38 component={Settings}
39/>
40const CustomWrapper = ({ isLoggedIn, ...props }) => {
41 const location = useLocation();
42 return isLoggedIn? (
43 <Outlet />
44 ) : (
45 <Navigate
46 to={`/login/${location.search}`}
47 replace
48 state={{ location }}
49 />
50 )
51};
52
...
1const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
2 return (
3 <Route
4 {...rest}
5 render={(props) =>
6 isLoggedIn? (
7 <Component {...props} />
8 ) : (
9 <Redirect
10 to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
11 />
12 )
13 }
14 />
15 );
16};
17
18
19<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />
20
21const CustomRoute = ({ isLoggedIn, ...props }) => {
22 const location = useLocation();
23 return isLoggedIn? (
24 <Route {...props} />
25 ) : (
26 <Redirect
27 to={{
28 pathname: `/login/${location.search}`,
29 state: { location },
30 }}
31 />
32 );
33};
34<PrivateRoute
35 exact
36 isLoggedIn={isLoggedIn}
37 path="/settings"
38 component={Settings}
39/>
40const CustomWrapper = ({ isLoggedIn, ...props }) => {
41 const location = useLocation();
42 return isLoggedIn? (
43 <Outlet />
44 ) : (
45 <Navigate
46 to={`/login/${location.search}`}
47 replace
48 state={{ location }}
49 />
50 )
51};
52<Route path="settings" element={<CustomWrapper isLoggedIn={isLoggedIn} />} >
53 <Route path="settings" element={<Settings />} />
54</Route>
55
QUESTION
Patch request not patching - 403 returned - django rest framework
Asked 2021-Dec-11 at 07:34I'm trying to test an API endpoint with a patch request to ensure it works.
I'm using APILiveServerTestCase
but can't seem to get the permissions required to patch the item. I created one user (adminuser
) who is a superadmin with access to everything and all permissions.
My test case looks like this:
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24
I've tried this test a number of different ways - all giving the same result (403).
I have setup the python debugger in the test, and I have tried actually going to http://localhost:xxxxx/admin/
in the browser and logging in manually with any user but the page just refreshes when I click to login and I never get 'logged in' to see the admin. I'm not sure if that's because it doesn't completely work from within a debugger like that or not.
My test looks like this (using the Requests library):
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52
I have tried it very similarly to the documentation using APIRequestFactory
and forcing authentication:
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52 def test_patch_request_updates_object(self):
53 data_dict = {
54 "signature": "TT",
55 "purchasing": "true",
56 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
57 }
58 url = self.full_url + str(self.future_vehicle.id) + "/"
59 api_req_factory = APIRequestFactory()
60 view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
61 api_request = api_req_factory.patch(
62 url, json.dumps(data_dict), content_type="application/json"
63 )
64 force_authenticate(api_request, self.admin_user)
65 response = view(api_request, pk=self.future_assignment.id)
66 fte_future_assignment = FutureVehicle.objects.filter(
67 id=self.future_assignment.id
68 ).first()
69 self.assertEqual(fte_future_assignment.signature, "TT")
70
If I enter the debugger to look at the responses, it's always a 403
.
The viewset
itself is very simple:
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52 def test_patch_request_updates_object(self):
53 data_dict = {
54 "signature": "TT",
55 "purchasing": "true",
56 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
57 }
58 url = self.full_url + str(self.future_vehicle.id) + "/"
59 api_req_factory = APIRequestFactory()
60 view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
61 api_request = api_req_factory.patch(
62 url, json.dumps(data_dict), content_type="application/json"
63 )
64 force_authenticate(api_request, self.admin_user)
65 response = view(api_request, pk=self.future_assignment.id)
66 fte_future_assignment = FutureVehicle.objects.filter(
67 id=self.future_assignment.id
68 ).first()
69 self.assertEqual(fte_future_assignment.signature, "TT")
70class FutureVehicleViewSet(ModelViewSet):
71 serializer_class = FutureVehicleSerializer
72
73 def get_queryset(self):
74 queryset = FutureVehicle.exclude_denied.all()
75 user_id = self.request.query_params.get("user_id", None)
76 if user_id:
77 queryset = queryset.filter(user_id=user_id)
78 return queryset
79
The serializer is just as basic as it gets - it's just the FutureVehicle
model and all fields.
I just can't figure out why my user won't login - or if maybe I'm doing something wrong in my attempts to patch?
I'm pretty new to Django Rest Framework in general, so any guidances is helpful!
Edit to add - my DRF Settings look like this:
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52 def test_patch_request_updates_object(self):
53 data_dict = {
54 "signature": "TT",
55 "purchasing": "true",
56 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
57 }
58 url = self.full_url + str(self.future_vehicle.id) + "/"
59 api_req_factory = APIRequestFactory()
60 view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
61 api_request = api_req_factory.patch(
62 url, json.dumps(data_dict), content_type="application/json"
63 )
64 force_authenticate(api_request, self.admin_user)
65 response = view(api_request, pk=self.future_assignment.id)
66 fte_future_assignment = FutureVehicle.objects.filter(
67 id=self.future_assignment.id
68 ).first()
69 self.assertEqual(fte_future_assignment.signature, "TT")
70class FutureVehicleViewSet(ModelViewSet):
71 serializer_class = FutureVehicleSerializer
72
73 def get_queryset(self):
74 queryset = FutureVehicle.exclude_denied.all()
75 user_id = self.request.query_params.get("user_id", None)
76 if user_id:
77 queryset = queryset.filter(user_id=user_id)
78 return queryset
79REST_FRAMEWORK = {
80 "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
81 "DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
82 "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
83 "DEFAULT_AUTHENTICATION_CLASSES": [
84 # Enabling this it will require Django Session (Including CSRF)
85 "rest_framework.authentication.SessionAuthentication"
86 ],
87 "DEFAULT_PERMISSION_CLASSES": [
88 # Globally only allow IsAuthenticated users access to API Endpoints
89 "rest_framework.permissions.IsAuthenticated"
90 ],
91}
92
I'm certain adminuser
is the user we wish to login - if I go into the debugger and check the users, they exist as a user. During creation, any user created has a password set to 'test'.
ANSWER
Answered 2021-Dec-11 at 07:34The test you have written is also testing the Django framework logic (ie: Django admin login). I recommend testing your own functionality, which occurs after login to the Django admin. Django's testing framework offers a helper for logging into the admin, client.login
. This allows you to focus on testing your own business logic/not need to maintain internal django authentication business logic tests, which may change release to release.
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52 def test_patch_request_updates_object(self):
53 data_dict = {
54 "signature": "TT",
55 "purchasing": "true",
56 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
57 }
58 url = self.full_url + str(self.future_vehicle.id) + "/"
59 api_req_factory = APIRequestFactory()
60 view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
61 api_request = api_req_factory.patch(
62 url, json.dumps(data_dict), content_type="application/json"
63 )
64 force_authenticate(api_request, self.admin_user)
65 response = view(api_request, pk=self.future_assignment.id)
66 fte_future_assignment = FutureVehicle.objects.filter(
67 id=self.future_assignment.id
68 ).first()
69 self.assertEqual(fte_future_assignment.signature, "TT")
70class FutureVehicleViewSet(ModelViewSet):
71 serializer_class = FutureVehicleSerializer
72
73 def get_queryset(self):
74 queryset = FutureVehicle.exclude_denied.all()
75 user_id = self.request.query_params.get("user_id", None)
76 if user_id:
77 queryset = queryset.filter(user_id=user_id)
78 return queryset
79REST_FRAMEWORK = {
80 "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
81 "DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
82 "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
83 "DEFAULT_AUTHENTICATION_CLASSES": [
84 # Enabling this it will require Django Session (Including CSRF)
85 "rest_framework.authentication.SessionAuthentication"
86 ],
87 "DEFAULT_PERMISSION_CLASSES": [
88 # Globally only allow IsAuthenticated users access to API Endpoints
89 "rest_framework.permissions.IsAuthenticated"
90 ],
91}
92from django.test import TestCase, Client
93
94
95def TestCase():
96 client.login(username=self.username, password=self.password)
97
However, if you must replicate and manage the business logic of what client.login
is doing, here's some of the business logic from Django:
1class FutureVehicleURLTest(APILiveServerTestCase):
2 def setUp(self):
3 # Setup users and some vehicle data we can query against
4 management.call_command("create_users_and_vehicle_data", verbosity=0)
5 self.user = UserFactory()
6 self.admin_user = User.objects.get(username="adminuser")
7 self.future_vehicle = f.FutureVehicleFactory(
8 user=self.user,
9 last_updated_by=self.user,
10 )
11 self.vehicle = f.VehicleFactory(
12 user=self.user,
13 created_by=self.user,
14 modified_by=self.user,
15 )
16 self.url = reverse("FutureVehicles-list")
17 self.full_url = self.live_server_url + self.url
18 time = str(datetime.now())
19 self.form_data = {
20 "signature": "TT",
21 "purchasing": True,
22 "confirmed_at": time,
23 }
24 def test_patch_request_updates_object(self):
25 data_dict = {
26 "signature": "TT",
27 "purchasing": "true",
28 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
29 }
30 url = self.full_url + str(self.future_vehicle.id) + "/"
31 client = requests.Session()
32 client.auth = HTTPBasicAuth(self.admin_user.username, "test")
33 client.headers.update({"x-test": "true"})
34 response = client.get(self.live_server_url + "/admin/")
35 csrftoken = response.cookies["csrftoken"]
36 # interact with the api
37 response = client.patch(
38 url,
39 data=json.dumps(data_dict),
40 cookies=response.cookies,
41 headers={
42 "X-Requested-With": "XMLHttpRequest",
43 "X-CSRFTOKEN": csrftoken,
44 },
45 )
46 # RESPONSE GIVES 403 PERMISSION DENIED
47 fte_future_vehicle = FutureVehicle.objects.filter(
48 id=self.future_vehicle.id
49 ).first()
50 # THIS ERRORS WITH '' not equal to 'TT'
51 self.assertEqual(fte_future_vehicle.signature, "TT")
52 def test_patch_request_updates_object(self):
53 data_dict = {
54 "signature": "TT",
55 "purchasing": "true",
56 "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
57 }
58 url = self.full_url + str(self.future_vehicle.id) + "/"
59 api_req_factory = APIRequestFactory()
60 view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
61 api_request = api_req_factory.patch(
62 url, json.dumps(data_dict), content_type="application/json"
63 )
64 force_authenticate(api_request, self.admin_user)
65 response = view(api_request, pk=self.future_assignment.id)
66 fte_future_assignment = FutureVehicle.objects.filter(
67 id=self.future_assignment.id
68 ).first()
69 self.assertEqual(fte_future_assignment.signature, "TT")
70class FutureVehicleViewSet(ModelViewSet):
71 serializer_class = FutureVehicleSerializer
72
73 def get_queryset(self):
74 queryset = FutureVehicle.exclude_denied.all()
75 user_id = self.request.query_params.get("user_id", None)
76 if user_id:
77 queryset = queryset.filter(user_id=user_id)
78 return queryset
79REST_FRAMEWORK = {
80 "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
81 "DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
82 "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
83 "DEFAULT_AUTHENTICATION_CLASSES": [
84 # Enabling this it will require Django Session (Including CSRF)
85 "rest_framework.authentication.SessionAuthentication"
86 ],
87 "DEFAULT_PERMISSION_CLASSES": [
88 # Globally only allow IsAuthenticated users access to API Endpoints
89 "rest_framework.permissions.IsAuthenticated"
90 ],
91}
92from django.test import TestCase, Client
93
94
95def TestCase():
96 client.login(username=self.username, password=self.password)
97 def login(self, **credentials):
98 """
99 Set the Factory to appear as if it has successfully logged into a site.
100 Return True if login is possible or False if the provided credentials
101 are incorrect.
102 """
103 from django.contrib.auth import authenticate
104 user = authenticate(**credentials)
105 if user:
106 self._login(user)
107 return True
108 return False
109
110 def force_login(self, user, backend=None):
111 def get_backend():
112 from django.contrib.auth import load_backend
113 for backend_path in settings.AUTHENTICATION_BACKENDS:
114 backend = load_backend(backend_path)
115 if hasattr(backend, 'get_user'):
116 return backend_path
117
118 if backend is None:
119 backend = get_backend()
120 user.backend = backend
121 self._login(user, backend)
122
123 def _login(self, user, backend=None):
124 from django.contrib.auth import login
125
126 # Create a fake request to store login details.
127 request = HttpRequest()
128 if self.session:
129 request.session = self.session
130 else:
131 engine = import_module(settings.SESSION_ENGINE)
132 request.session = engine.SessionStore()
133 login(request, user, backend)
134 # Save the session values.
135 request.session.save()
136 # Set the cookie to represent the session.
137 session_cookie = settings.SESSION_COOKIE_NAME
138 self.cookies[session_cookie] = request.session.session_key
139 cookie_data = {
140 'max-age': None,
141 'path': '/',
142 'domain': settings.SESSION_COOKIE_DOMAIN,
143 'secure': settings.SESSION_COOKIE_SECURE or None,
144 'expires': None,
145 }
146 self.cookies[session_cookie].update(cookie_data)
147
Django client.login: https://github.com/django/django/blob/main/django/test/client.py#L596-L646
QUESTION
Haskell: Can I read integers directly into an array?
Asked 2021-Dec-05 at 11:40In this programming problem, the input is an n
×m
integer matrix. Typically, n
≈ 105 and m
≈ 10. The official solution (1606D, Tutorial) is quite imperative: it involves some matrix manipulation, precomputation and aggregation. For fun, I took it as an STUArray implementation exercise.
I have managed to implement it using STUArray, but still the program takes way more memory than permitted (256MB). Even when run locally, the maximum resident set size is >400 MB. On profiling, reading from stdin seems to be dominating the memory footprint:
Functions readv
and readv.readInt
, responsible for parsing integers and saving them into a 2D list, are taking around 50-70 MB, as opposed to around 16 MB = (106 integers) × (8 bytes per integer + 8 bytes per link).
Is there a hope I can get the total memory below 256 MB? I'm already using Text
package for input. Maybe I should avoid lists altogether and directly read integers from stdin to the array. How can we do that? Or, is the issue elsewhere?
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76
Quick description of the code above:
- Read
n
rows each havingm
integers. - Sort the rows by their first element.
- Now compute four 'prefix matrices', one from each corner. For top-left and bottom-right corners, it's the prefix-maximum, and for the other two corners, it's the prefix-minimum that we need to compute.
- Find a cell [i,j] at which these prefix matrices satisfy the following condition: top_left [i,j] < bottom_left [i,j] and top_right [i,j] > bottom_right [i,j]
- For rows 1 through i, mark their original indices (i.e. position in the unsorted input matrix) as Blue. Mark the rest as Red.
Sample input and Commands
Sample input: inp3.txt.
Command:
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76> stack ghc -- -main-is CF1606D.main -with-rtsopts="-s -h -p -P" -rtsopts -prof -fprof-auto CF1606D
77> gtime -v ./CF1606D < inp3.txt > outp
78 ...
79 ...
80 MUT time 2.990s ( 3.744s elapsed) # RTS -s output
81 GC time 4.525s ( 6.231s elapsed) # RTS -s output
82 ...
83 ...
84 Maximum resident set size (kbytes): 408532 # >256 MB (gtime output)
85
86> stack exec -- hp2ps -t0.1 -e8in -c CF1606D.hp && open CF1606D.ps
87
Question about GC: As shown above in the +RTS -s output, GC seems to be taking longer than the actual logic execution. Is this normal? Is there a way to visualize the GC activity over time? I tried making matrices strict but that didn't have any impact.
Probably this is not a functional-friendly problem at all (although I'll be happy to be disproved on this). For example, Java uses GC too but there are lots of successful Java submissions. Still, I want to see how far I can push. Thanks!
ANSWER
Answered 2021-Dec-05 at 11:40Contrary to common belief Haskell is quite friendly with respect to problems like that. The real issue is that the array
library that comes with GHC is total garbage. Another big problem is that everyone is taught in Haskell to use lists where arrays should be used instead, which is usually one of the major sources of slow code and memory bloated programs. So, it is not surprising that GC takes a long time, it is because there is way too much stuff being allocation. Here is a run on the supplied input for the solution provided below:
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76> stack ghc -- -main-is CF1606D.main -with-rtsopts="-s -h -p -P" -rtsopts -prof -fprof-auto CF1606D
77> gtime -v ./CF1606D < inp3.txt > outp
78 ...
79 ...
80 MUT time 2.990s ( 3.744s elapsed) # RTS -s output
81 GC time 4.525s ( 6.231s elapsed) # RTS -s output
82 ...
83 ...
84 Maximum resident set size (kbytes): 408532 # >256 MB (gtime output)
85
86> stack exec -- hp2ps -t0.1 -e8in -c CF1606D.hp && open CF1606D.ps
87 1,483,547,096 bytes allocated in the heap
88 566,448 bytes copied during GC
89 18,703,640 bytes maximum residency (3 sample(s))
90 1,223,400 bytes maximum slop
91 32 MiB total memory in use (0 MB lost due to fragmentation)
92
93 Tot time (elapsed) Avg pause Max pause
94 Gen 0 1399 colls, 0 par 0.009s 0.009s 0.0000s 0.0011s
95 Gen 1 3 colls, 0 par 0.002s 0.002s 0.0006s 0.0016s
96
97 TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)
98
99 SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
100
101 INIT time 0.001s ( 0.001s elapsed)
102 MUT time 0.484s ( 0.517s elapsed)
103 GC time 0.011s ( 0.011s elapsed)
104 EXIT time 0.001s ( 0.002s elapsed)
105 Total time 0.496s ( 0.530s elapsed)
106
The solution provided below uses an array library massiv
, which makes it impossible to submit to codeforces. However, hopefully the goal is to get better at Haskell, rather than get points on some website.
The red-blue matrix can be separated into two stages: read and solve
Read Read the dimensionsIn the main
function we only read total number of arrays and dimensions for each array. Also we print the outcome. Nothing exciting here. (Note that the linked file inp3.txt
has a larger array than the limits defined in the problem: n*m <= 10^6
)
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76> stack ghc -- -main-is CF1606D.main -with-rtsopts="-s -h -p -P" -rtsopts -prof -fprof-auto CF1606D
77> gtime -v ./CF1606D < inp3.txt > outp
78 ...
79 ...
80 MUT time 2.990s ( 3.744s elapsed) # RTS -s output
81 GC time 4.525s ( 6.231s elapsed) # RTS -s output
82 ...
83 ...
84 Maximum resident set size (kbytes): 408532 # >256 MB (gtime output)
85
86> stack exec -- hp2ps -t0.1 -e8in -c CF1606D.hp && open CF1606D.ps
87 1,483,547,096 bytes allocated in the heap
88 566,448 bytes copied during GC
89 18,703,640 bytes maximum residency (3 sample(s))
90 1,223,400 bytes maximum slop
91 32 MiB total memory in use (0 MB lost due to fragmentation)
92
93 Tot time (elapsed) Avg pause Max pause
94 Gen 0 1399 colls, 0 par 0.009s 0.009s 0.0000s 0.0011s
95 Gen 1 3 colls, 0 par 0.002s 0.002s 0.0006s 0.0016s
96
97 TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)
98
99 SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
100
101 INIT time 0.001s ( 0.001s elapsed)
102 MUT time 0.484s ( 0.517s elapsed)
103 GC time 0.011s ( 0.011s elapsed)
104 EXIT time 0.001s ( 0.002s elapsed)
105 Total time 0.496s ( 0.530s elapsed)
106import Control.Monad.ST
107import Control.Monad
108import qualified Data.ByteString as BS
109import Data.Massiv.Array as A hiding (B)
110import Data.Massiv.Array.Mutable.Algorithms (quicksortByM_)
111import Control.Scheduler (trivialScheduler_)
112
113main :: IO ()
114main = do
115 t <- Prelude.read <$> getLine
116 when (t < 1 || t > 1000) $ error $ "Invalid t: " ++ show t
117 replicateM_ t $ do
118 dimsStr <- getLine
119 case Prelude.map Prelude.read (words dimsStr) of
120 -- Test file fails this check: && n * m <= 10 ^ (6 :: Int) -> do
121 [n, m] | n >= 2 && m > 0 && m <= 5 * 10 ^ (5 :: Int) -> do
122 mat <- readMatrix n m
123 case solve mat of
124 Nothing -> putStrLn "NO"
125 Just (ix, cs) -> do
126 putStrLn "YES"
127 putStr $ foldMap show cs
128 putStr " "
129 print ix
130 _ -> putStrLn $ "Unexpected dimensions: " ++ show dimsStr
131
Loading the input into array is the major source of problems int the original question:
- there is no need to rely on
text
, ascii characters is the only valid input expected by the problem. - input is read into a list of lists. That list of lists is the real source of the memory overhead.
- Sorting lists is ridiculously slow and memory hungry.
Normally in such situation it would be much better to read input in a streaming fashion using something like conduit
. In particular, reading input as stream of bytes and parsing those bytes as numbers would be the optimal solution. That being said there are hard requirements on the width of each array in the description of the problem, so we can get away with reading input line-by-line as a ByteString
and then parsing numbers (assumed unsigned for simplicity) in each line and write those numbers into array at the same time. This ensures that at this stage we will only have allocated the resulting array and a single line as sequence of bytes. This could be done cleaner with a parsing library like attoparsec
, but problem is simple enough to just do it adhoc.
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76> stack ghc -- -main-is CF1606D.main -with-rtsopts="-s -h -p -P" -rtsopts -prof -fprof-auto CF1606D
77> gtime -v ./CF1606D < inp3.txt > outp
78 ...
79 ...
80 MUT time 2.990s ( 3.744s elapsed) # RTS -s output
81 GC time 4.525s ( 6.231s elapsed) # RTS -s output
82 ...
83 ...
84 Maximum resident set size (kbytes): 408532 # >256 MB (gtime output)
85
86> stack exec -- hp2ps -t0.1 -e8in -c CF1606D.hp && open CF1606D.ps
87 1,483,547,096 bytes allocated in the heap
88 566,448 bytes copied during GC
89 18,703,640 bytes maximum residency (3 sample(s))
90 1,223,400 bytes maximum slop
91 32 MiB total memory in use (0 MB lost due to fragmentation)
92
93 Tot time (elapsed) Avg pause Max pause
94 Gen 0 1399 colls, 0 par 0.009s 0.009s 0.0000s 0.0011s
95 Gen 1 3 colls, 0 par 0.002s 0.002s 0.0006s 0.0016s
96
97 TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)
98
99 SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
100
101 INIT time 0.001s ( 0.001s elapsed)
102 MUT time 0.484s ( 0.517s elapsed)
103 GC time 0.011s ( 0.011s elapsed)
104 EXIT time 0.001s ( 0.002s elapsed)
105 Total time 0.496s ( 0.530s elapsed)
106import Control.Monad.ST
107import Control.Monad
108import qualified Data.ByteString as BS
109import Data.Massiv.Array as A hiding (B)
110import Data.Massiv.Array.Mutable.Algorithms (quicksortByM_)
111import Control.Scheduler (trivialScheduler_)
112
113main :: IO ()
114main = do
115 t <- Prelude.read <$> getLine
116 when (t < 1 || t > 1000) $ error $ "Invalid t: " ++ show t
117 replicateM_ t $ do
118 dimsStr <- getLine
119 case Prelude.map Prelude.read (words dimsStr) of
120 -- Test file fails this check: && n * m <= 10 ^ (6 :: Int) -> do
121 [n, m] | n >= 2 && m > 0 && m <= 5 * 10 ^ (5 :: Int) -> do
122 mat <- readMatrix n m
123 case solve mat of
124 Nothing -> putStrLn "NO"
125 Just (ix, cs) -> do
126 putStrLn "YES"
127 putStr $ foldMap show cs
128 putStr " "
129 print ix
130 _ -> putStrLn $ "Unexpected dimensions: " ++ show dimsStr
131type Val = Word
132
133readMatrix :: Int -> Int -> IO (Matrix P Val)
134readMatrix n m = createArrayS_ (Sz2 n m) readMMatrix
135
136readMMatrix :: MMatrix RealWorld P Val -> IO ()
137readMMatrix mat =
138 loopM_ 0 (< n) (+ 1) $ \i -> do
139 line <- BS.getLine
140 --- ^ reads at most 10Mb because it is known that input will be at most
141 -- 5*10^5 Words: 19 digits max per Word and one for space: 5*10^5 * 20bytes
142 loopM 0 (< m) (+ 1) line $ \j bs ->
143 let (word, bs') = parseWord bs
144 in bs' <$ write_ mat (i :. j) word
145 where
146 Sz2 n m = sizeOfMArray mat
147 isSpace = (== 32)
148 isDigit w8 = w8 >= 48 && w8 <= 57
149 parseWord bs =
150 case BS.uncons bs of
151 Just (w8, bs')
152 | isDigit w8 -> parseWordLoop (fromIntegral (w8 - 48)) bs'
153 | otherwise -> error $ "Unexpected byte: " ++ show w8
154 Nothing -> error "Unexpected end of input"
155 parseWordLoop !acc bs =
156 case BS.uncons bs of
157 Nothing -> (acc, bs)
158 Just (w8, bs')
159 | isSpace w8 -> (acc, bs')
160 | isDigit w8 -> parseWordLoop (acc * 10 + fromIntegral (w8 - 48)) bs'
161 | otherwise -> error $ "Unexpected byte: " ++ show w8
162
This is the step where we implement the actual solution. Instead of going into trying to fix the solution provided in this SO question I went on and translated the C++ solution that was linked in the question instead. Reason I went that route is twofold:
- C++ soluition is highly imperative and I wanted to demonstrate that imperative array manipulations are not that foreign to Haskell, so I tried to create a translation that was as close as possible.
- I knew that solution works
Note, that it should be possible to rewrite the solution below with array
package, because in the end all that is needed are the read
, write
and allocate
operations.
1{-# OPTIONS_GHC -O2 #-}
2module CF1606D where
3import qualified Data.Text as T
4import qualified Data.Text.IO as TI
5import qualified Data.Text.Read as TR
6import Control.Monad
7import qualified Data.List as DL
8import qualified Data.IntSet as DS
9import Control.Monad.ST
10import Data.Array.ST.Safe
11import Data.Int (Int32)
12import Data.Array.Unboxed
13
14solve :: IO ()
15solve = do
16 ~[n,m] <- readv
17 -- 2D list
18 input <- {-# SCC input #-} replicateM (fromIntegral n) readv
19 let
20 ints = [1..]
21 sorted = DL.sortOn (head.fst) (zip input ints)
22 (rows,indices) = {-# SCC rows_inds #-} unzip sorted
23 -- 2D list converted into matrix:
24 matrix = mat (fromIntegral n) (fromIntegral m) rows
25 infinite = 10^7
26 asc x y = [x,x+1..y]
27 desc x y = [y,y-1..x]
28 -- Four prefix-matrices:
29 tlMax = runSTUArray $ prefixMat max 0 asc asc (subtract 1) (subtract 1) =<< matrix
30 blMin = runSTUArray $ prefixMat min infinite desc asc (+1) (subtract 1) =<< matrix
31 trMin = runSTUArray $ prefixMat min infinite asc desc (subtract 1) (+1) =<< matrix
32 brMax = runSTUArray $ prefixMat max 0 desc desc (+1) (+1) =<< matrix
33 good _ (i,j)
34 | tlMax!(i,j) < blMin!(i+1,j) && brMax!(i+1,j+1) < trMin!(i,j+1) = Left (i,j)
35 | otherwise = Right ()
36 {-# INLINABLE good #-}
37 nearAns = foldM good () [(i,j)|i<-[1..n-1],j<-[1..m-1]]
38 ans = either (\(i,j)-> "YES\n" ++ color n (take i indices) ++ " " ++ show j) (const "NO") nearAns
39 putStrLn ans
40
41type I = Int32
42type S s = (STUArray s (Int, Int) I)
43type R = Int -> Int -> [Int]
44type F = Int -> Int
45
46mat :: Int -> Int -> [[I]] -> ST s (S s)
47mat n m rows = newListArray ((1,1),(n,m)) $ concat rows
48
49prefixMat :: (I->I->I) -> I -> R -> R -> F -> F -> S s -> ST s (S s)
50prefixMat opt worst ordi ordj previ prevj mat = do
51 ((ilo,jlo),(ihi,jhi)) <- getBounds mat
52 pre <- newArray ((ilo-1,jlo-1),(ihi+1,jhi+1)) worst
53 forM_ (ordi ilo ihi) $ \i-> do
54 forM_ (ordj jlo jhi) $ \j -> do
55 matij <- readArray mat (i,j)
56 prei <- readArray pre (previ i,j)
57 prej <- readArray pre (i, prevj j)
58 writeArray pre (i,j) (opt (opt prei prej) matij)
59 return pre
60
61color :: Int -> [Int] -> String
62color n inds = let
63 temp = DS.fromList inds
64 colors = [if DS.member i temp then 'B' else 'R' | i<-[1..n]]
65 in colors
66
67readv :: Integral t => IO [t]
68readv = map readInt . T.words <$> TI.getLine where
69 readInt = fromIntegral . either (const 0) fst . TR.signed TR.decimal
70{-# INLINABLE readv #-}
71
72main :: IO ()
73main = do
74 ~[n] <- readv
75 replicateM_ n solve
76> stack ghc -- -main-is CF1606D.main -with-rtsopts="-s -h -p -P" -rtsopts -prof -fprof-auto CF1606D
77> gtime -v ./CF1606D < inp3.txt > outp
78 ...
79 ...
80 MUT time 2.990s ( 3.744s elapsed) # RTS -s output
81 GC time 4.525s ( 6.231s elapsed) # RTS -s output
82 ...
83 ...
84 Maximum resident set size (kbytes): 408532 # >256 MB (gtime output)
85
86> stack exec -- hp2ps -t0.1 -e8in -c CF1606D.hp && open CF1606D.ps
87 1,483,547,096 bytes allocated in the heap
88 566,448 bytes copied during GC
89 18,703,640 bytes maximum residency (3 sample(s))
90 1,223,400 bytes maximum slop
91 32 MiB total memory in use (0 MB lost due to fragmentation)
92
93 Tot time (elapsed) Avg pause Max pause
94 Gen 0 1399 colls, 0 par 0.009s 0.009s 0.0000s 0.0011s
95 Gen 1 3 colls, 0 par 0.002s 0.002s 0.0006s 0.0016s
96
97 TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)
98
99 SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
100
101 INIT time 0.001s ( 0.001s elapsed)
102 MUT time 0.484s ( 0.517s elapsed)
103 GC time 0.011s ( 0.011s elapsed)
104 EXIT time 0.001s ( 0.002s elapsed)
105 Total time 0.496s ( 0.530s elapsed)
106import Control.Monad.ST
107import Control.Monad
108import qualified Data.ByteString as BS
109import Data.Massiv.Array as A hiding (B)
110import Data.Massiv.Array.Mutable.Algorithms (quicksortByM_)
111import Control.Scheduler (trivialScheduler_)
112
113main :: IO ()
114main = do
115 t <- Prelude.read <$> getLine
116 when (t < 1 || t > 1000) $ error $ "Invalid t: " ++ show t
117 replicateM_ t $ do
118 dimsStr <- getLine
119 case Prelude.map Prelude.read (words dimsStr) of
120 -- Test file fails this check: && n * m <= 10 ^ (6 :: Int) -> do
121 [n, m] | n >= 2 && m > 0 && m <= 5 * 10 ^ (5 :: Int) -> do
122 mat <- readMatrix n m
123 case solve mat of
124 Nothing -> putStrLn "NO"
125 Just (ix, cs) -> do
126 putStrLn "YES"
127 putStr $ foldMap show cs
128 putStr " "
129 print ix
130 _ -> putStrLn $ "Unexpected dimensions: " ++ show dimsStr
131type Val = Word
132
133readMatrix :: Int -> Int -> IO (Matrix P Val)
134readMatrix n m = createArrayS_ (Sz2 n m) readMMatrix
135
136readMMatrix :: MMatrix RealWorld P Val -> IO ()
137readMMatrix mat =
138 loopM_ 0 (< n) (+ 1) $ \i -> do
139 line <- BS.getLine
140 --- ^ reads at most 10Mb because it is known that input will be at most
141 -- 5*10^5 Words: 19 digits max per Word and one for space: 5*10^5 * 20bytes
142 loopM 0 (< m) (+ 1) line $ \j bs ->
143 let (word, bs') = parseWord bs
144 in bs' <$ write_ mat (i :. j) word
145 where
146 Sz2 n m = sizeOfMArray mat
147 isSpace = (== 32)
148 isDigit w8 = w8 >= 48 && w8 <= 57
149 parseWord bs =
150 case BS.uncons bs of
151 Just (w8, bs')
152 | isDigit w8 -> parseWordLoop (fromIntegral (w8 - 48)) bs'
153 | otherwise -> error $ "Unexpected byte: " ++ show w8
154 Nothing -> error "Unexpected end of input"
155 parseWordLoop !acc bs =
156 case BS.uncons bs of
157 Nothing -> (acc, bs)
158 Just (w8, bs')
159 | isSpace w8 -> (acc, bs')
160 | isDigit w8 -> parseWordLoop (acc * 10 + fromIntegral (w8 - 48)) bs'
161 | otherwise -> error $ "Unexpected byte: " ++ show w8
162computeSortBy ::
163 (Load r Ix1 e, Manifest r' e)
164 => (e -> e -> Ordering)
165 -> Vector r e
166 -> Vector r' e
167computeSortBy f vec =
168 withLoadMArrayST_ vec $ quicksortByM_ (\x y -> pure $ f x y) trivialScheduler_
169
170solve :: Matrix P Val -> Maybe (Int, [Color])
171solve a = runST $ do
172 let sz@(Sz2 n m) = size a
173 ord :: Vector P Int
174 ord = computeSortBy
175 (\x y -> compare (a ! (y :. 0)) (a ! (x :. 0))) (0 ..: n)
176 mxl <- newMArray @P sz minBound
177 loopM_ (n - 1) (>= 0) (subtract 1) $ \ i ->
178 loopM_ 0 (< m) (+ 1) $ \j -> do
179 writeM mxl (i :. j) (a ! ((ord ! i) :. j))
180 when (i < n - 1) $
181 writeM mxl (i :. j)
182 =<< max <$> readM mxl (i :. j) <*> readM mxl (i + 1 :. j)
183 when (j > 0) $
184 writeM mxl (i :. j)
185 =<< max <$> readM mxl (i :. j) <*> readM mxl (i :. j - 1)
186 mnr <- newMArray @P sz maxBound
187 loopM_ (n - 1) (>= 0) (subtract 1) $ \ i ->
188 loopM_ (m - 1) (>= 0) (subtract 1) $ \ j -> do
189 writeM mnr (i :. j) (a ! ((ord ! i) :. j))
190 when (i < n - 1) $
191 writeM mnr (i :. j)
192 =<< min <$> readM mnr (i :. j) <*> readM mnr (i + 1 :. j)
193 when (j < m - 1) $
194 writeM mnr (i :. j)
195 =<< min <$> readM mnr (i :. j) <*> readM mnr (i :. j + 1)
196 mnl <- newMArray @P (Sz m) maxBound
197 mxr <- newMArray @P (Sz m) minBound
198 let goI i
199 | i < n - 1 = do
200 loopM_ 0 (< m) (+ 1) $ \j -> do
201 val <- min (a ! ((ord ! i) :. j)) <$> readM mnl j
202 writeM mnl j val
203 when (j > 0) $
204 writeM mnl j . min val =<< readM mnl (j - 1)
205 loopM_ (m - 1) (>= 0) (subtract 1) $ \j -> do
206 val <- max (a ! ((ord ! i) :. j)) <$> readM mxr j
207 writeM mxr j val
208 when (j < m - 1) $
209 writeM mxr j . max val =<< readM mxr (j + 1)
210 let goJ j
211 | j < m - 1 = do
212 mnlVal <- readM mnl j
213 mxlVal <- readM mxl (i + 1 :. j)
214 mxrVal <- readM mxr (j + 1)
215 mnrVal <- readM mnr ((i + 1) :. (j + 1))
216 if mnlVal > mxlVal && mxrVal < mnrVal
217 then pure $ Just (i, j)
218 else goJ (j + 1)
219 | otherwise = pure Nothing
220 goJ 0 >>= \case
221 Nothing -> goI (i + 1)
222 Just pair -> pure $ Just pair
223 | otherwise = pure Nothing
224 mAns <- goI 0
225 Control.Monad.forM mAns $ \ (ansFirst, ansSecond) -> do
226 resVec <- createArrayS_ @BL (Sz n) $ \res ->
227 iforM_ ord $ \i ordIx -> do
228 writeM res ordIx $! if i <= ansFirst then R else B
229 pure (ansSecond + 1, A.toList resVec)
230
QUESTION
Typescript: deep keyof of a nested object, with related type
Asked 2021-Dec-02 at 09:30I'm looking for a way to have all keys / values pair of a nested object.
(For the autocomplete of MongoDB dot notation key / value type)
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9
Here is what I want to achieve, to make it becomes:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16
In this answer, I can get the key with Leaves<IPerson>
.
So it becomes 'name' | 'age' | 'contact.address' | 'contact.visitDate'
.
And in another answer from @jcalz, I can get the deep, related value type, with DeepIndex<IPerson, ...>
.
Is it possible to group them together, to become type like TPerson
?
When I start this question, I was thinking it could be as easy as something like [K in keyof T]: T[K];
, with some clever transformation. But I was wrong. Here is what I need:
So the interface
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30
No need to check for valid number
, the nature of Array / Index Signature should allow any number of elements.
The interface
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38
Tuple should be the one which cares about valid index numbers.
3. Readonlyreadonly
attributes should be removed from the final structure.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46
The use case is for MongoDB, the _id
, _created_date
cannot be modified after the data has been created. _id: never
is not working in this case, since it will block the creation of TPerson
.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60
It's sufficient just to bring the optional flags to transformed structure.
5. Intersection1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68
The interface
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71
becomes
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74
not
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81
We can give a list of Types to be the end node.
Here is what I don't need:- Union. It could be too complex with it.
- Class related keyword. No need to handle keywords ex: private / abstract .
- All the rest I didn't write it here.
ANSWER
Answered 2021-Dec-02 at 09:30In order to achieve this goal we need to create permutation of all allowed paths. For example:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92
Problem becomes more interesting with arrays and empty tuples.
Tuple, the array with explicit length, should be managed in this way:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102
Logic is straitforward. But how we can handle number[]
? There is no guarantee that index 1
exists.
I have decided to use user.arr.${number}
.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112
We still have 1 problem. Empty tuple. Array with zero elements - []
. Do we need to allow indexing at all? I don't know. I decided to use -1
.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122
I think the most important thing here is some convention. We can also use stringified `"never". I think it is up to OP how to handle it.
Since we know how we need to handle different cases we can start our implementation. Before we continue, we need to define several helpers.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155
I think naming and tests are self explanatory. At least I want to believe :D
Now, when we have all set of our utils, we can define our main util:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155/**
156 * If Cache is empty return Prop without dot,
157 * to avoid ".user"
158 */
159type HandleDot<
160 Cache extends string,
161 Prop extends string | number
162 > =
163 Cache extends ''
164 ? `${Prop}`
165 : `${Cache}.${Prop}`
166
167/**
168 * Simple iteration through object properties
169 */
170type HandleObject<Obj, Cache extends string> = {
171 [Prop in keyof Obj]:
172 // concat previous Cacha and Prop
173 | HandleDot<Cache, Prop & string>
174 // with next Cache and Prop
175 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
176}[keyof Obj]
177
178type Path<Obj, Cache extends string = ''> =
179 // if Obj is primitive
180 (Obj extends PropertyKey
181 // return Cache
182 ? Cache
183 // if Obj is Array (can be array, tuple, empty tuple)
184 : (Obj extends Array<unknown>
185 // and is tuple
186 ? (IsTuple<Obj> extends true
187 // and tuple is empty
188 ? (IsEmptyTuple<Obj> extends true
189 // call recursively Path with `-1` as an allowed index
190 ? Path<PropertyKey, HandleDot<Cache, -1>>
191 // if tuple is not empty we can handle it as regular object
192 : HandleObject<Obj, Cache>)
193 // if Obj is regular array call Path with union of all elements
194 : Path<Obj[number], HandleDot<Cache, number>>)
195 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
196 : HandleObject<Obj, Cache>)
197 )
198
199// "user" | "user.arr" | `user.arr.${number}`
200type Test = Extract<Path<Structure>, string>
201
There is small issue. We should not return highest level props, like user
. We need paths with at least one dot.
There are two ways:
- extract all props without dots
- provide extra generic parameter for indexing the level.
Two options are easy to implement.
Obtain all props with dot (.)
:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155/**
156 * If Cache is empty return Prop without dot,
157 * to avoid ".user"
158 */
159type HandleDot<
160 Cache extends string,
161 Prop extends string | number
162 > =
163 Cache extends ''
164 ? `${Prop}`
165 : `${Cache}.${Prop}`
166
167/**
168 * Simple iteration through object properties
169 */
170type HandleObject<Obj, Cache extends string> = {
171 [Prop in keyof Obj]:
172 // concat previous Cacha and Prop
173 | HandleDot<Cache, Prop & string>
174 // with next Cache and Prop
175 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
176}[keyof Obj]
177
178type Path<Obj, Cache extends string = ''> =
179 // if Obj is primitive
180 (Obj extends PropertyKey
181 // return Cache
182 ? Cache
183 // if Obj is Array (can be array, tuple, empty tuple)
184 : (Obj extends Array<unknown>
185 // and is tuple
186 ? (IsTuple<Obj> extends true
187 // and tuple is empty
188 ? (IsEmptyTuple<Obj> extends true
189 // call recursively Path with `-1` as an allowed index
190 ? Path<PropertyKey, HandleDot<Cache, -1>>
191 // if tuple is not empty we can handle it as regular object
192 : HandleObject<Obj, Cache>)
193 // if Obj is regular array call Path with union of all elements
194 : Path<Obj[number], HandleDot<Cache, number>>)
195 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
196 : HandleObject<Obj, Cache>)
197 )
198
199// "user" | "user.arr" | `user.arr.${number}`
200type Test = Extract<Path<Structure>, string>
201type WithDot<T extends string> = T extends `${string}.${string}` ? T : never
202
While above util is readable and maintainable, second one is a bit harder. We need to provide extra generic parameter in both Path
and HandleObject
.
See this example taken from other question / article:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155/**
156 * If Cache is empty return Prop without dot,
157 * to avoid ".user"
158 */
159type HandleDot<
160 Cache extends string,
161 Prop extends string | number
162 > =
163 Cache extends ''
164 ? `${Prop}`
165 : `${Cache}.${Prop}`
166
167/**
168 * Simple iteration through object properties
169 */
170type HandleObject<Obj, Cache extends string> = {
171 [Prop in keyof Obj]:
172 // concat previous Cacha and Prop
173 | HandleDot<Cache, Prop & string>
174 // with next Cache and Prop
175 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
176}[keyof Obj]
177
178type Path<Obj, Cache extends string = ''> =
179 // if Obj is primitive
180 (Obj extends PropertyKey
181 // return Cache
182 ? Cache
183 // if Obj is Array (can be array, tuple, empty tuple)
184 : (Obj extends Array<unknown>
185 // and is tuple
186 ? (IsTuple<Obj> extends true
187 // and tuple is empty
188 ? (IsEmptyTuple<Obj> extends true
189 // call recursively Path with `-1` as an allowed index
190 ? Path<PropertyKey, HandleDot<Cache, -1>>
191 // if tuple is not empty we can handle it as regular object
192 : HandleObject<Obj, Cache>)
193 // if Obj is regular array call Path with union of all elements
194 : Path<Obj[number], HandleDot<Cache, number>>)
195 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
196 : HandleObject<Obj, Cache>)
197 )
198
199// "user" | "user.arr" | `user.arr.${number}`
200type Test = Extract<Path<Structure>, string>
201type WithDot<T extends string> = T extends `${string}.${string}` ? T : never
202type KeysUnion<T, Cache extends string = '', Level extends any[] = []> =
203 T extends PropertyKey ? Cache : {
204 [P in keyof T]:
205 P extends string
206 ? Cache extends ''
207 ? KeysUnion<T[P], `${P}`, [...Level, 1]>
208 : Level['length'] extends 1 // if it is a higher level - proceed
209 ? KeysUnion<T[P], `${Cache}.${P}`, [...Level, 1]>
210 : Level['length'] extends 2 // stop on second level
211 ? Cache | KeysUnion<T[P], `${Cache}`, [...Level, 1]>
212 : never
213 : never
214 }[keyof T]
215
Honestly, I don't think it will be easy for any one to read this.
We need to implement one more thing. We need to obtain a value by computed path.
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155/**
156 * If Cache is empty return Prop without dot,
157 * to avoid ".user"
158 */
159type HandleDot<
160 Cache extends string,
161 Prop extends string | number
162 > =
163 Cache extends ''
164 ? `${Prop}`
165 : `${Cache}.${Prop}`
166
167/**
168 * Simple iteration through object properties
169 */
170type HandleObject<Obj, Cache extends string> = {
171 [Prop in keyof Obj]:
172 // concat previous Cacha and Prop
173 | HandleDot<Cache, Prop & string>
174 // with next Cache and Prop
175 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
176}[keyof Obj]
177
178type Path<Obj, Cache extends string = ''> =
179 // if Obj is primitive
180 (Obj extends PropertyKey
181 // return Cache
182 ? Cache
183 // if Obj is Array (can be array, tuple, empty tuple)
184 : (Obj extends Array<unknown>
185 // and is tuple
186 ? (IsTuple<Obj> extends true
187 // and tuple is empty
188 ? (IsEmptyTuple<Obj> extends true
189 // call recursively Path with `-1` as an allowed index
190 ? Path<PropertyKey, HandleDot<Cache, -1>>
191 // if tuple is not empty we can handle it as regular object
192 : HandleObject<Obj, Cache>)
193 // if Obj is regular array call Path with union of all elements
194 : Path<Obj[number], HandleDot<Cache, number>>)
195 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
196 : HandleObject<Obj, Cache>)
197 )
198
199// "user" | "user.arr" | `user.arr.${number}`
200type Test = Extract<Path<Structure>, string>
201type WithDot<T extends string> = T extends `${string}.${string}` ? T : never
202type KeysUnion<T, Cache extends string = '', Level extends any[] = []> =
203 T extends PropertyKey ? Cache : {
204 [P in keyof T]:
205 P extends string
206 ? Cache extends ''
207 ? KeysUnion<T[P], `${P}`, [...Level, 1]>
208 : Level['length'] extends 1 // if it is a higher level - proceed
209 ? KeysUnion<T[P], `${Cache}.${P}`, [...Level, 1]>
210 : Level['length'] extends 2 // stop on second level
211 ? Cache | KeysUnion<T[P], `${Cache}`, [...Level, 1]>
212 : never
213 : never
214 }[keyof T]
215
216type Acc = Record<string, any>
217
218type ReducerCallback<Accumulator extends Acc, El extends string> =
219 El extends keyof Accumulator ? Accumulator[El] : Accumulator
220
221type Reducer<
222 Keys extends string,
223 Accumulator extends Acc = {}
224 > =
225 // Key destructure
226 Keys extends `${infer Prop}.${infer Rest}`
227 // call Reducer with callback, just like in JS
228 ? Reducer<Rest, ReducerCallback<Accumulator, Prop>>
229 // this is the last part of path because no dot
230 : Keys extends `${infer Last}`
231 // call reducer with last part
232 ? ReducerCallback<Accumulator, Last>
233 : never
234
235{
236 type _ = Reducer<'user.arr', Structure> // []
237 type __ = Reducer<'user', Structure> // { arr: [] }
238}
239
You can find more information about using Reduce
in my blog.
Whole code:
1interface IPerson {
2 name: string;
3 age: number;
4 contact: {
5 address: string;
6 visitDate: Date;
7 }
8}
9type TPerson = {
10 name: string;
11 age: number;
12 contact: { address: string; visitDate: Date; }
13 "contact.address": string;
14 "contact.visitDate": Date;
15}
16interface IPerson {
17 contact: {
18 address: string;
19 visitDate: Date;
20 }[]
21}
22type TPerson = {
23 [x: `contact.${number}.address`]: string;
24 [x: `contact.${number}.visitDate`]: Date;
25 contact: {
26 address: string;
27 visitDate: Date;
28 }[];
29}
30interface IPerson {
31 contact: [string, Date]
32}
33type TPerson = {
34 [x: `contact.0`]: string;
35 [x: `contact.1`]: Date;
36 contact: [string, Date];
37}
38interface IPerson {
39 readonly _id: string;
40 age: number;
41 readonly _created_date: Date;
42}
43type TPerson = {
44 age: number;
45}
46interface IPerson {
47 contact: {
48 address: string;
49 visitDate?: Date;
50 }[];
51}
52type TPerson = {
53 [x: `contact.${number}.address`]: string;
54 [x: `contact.${number}.visitDate`]?: Date;
55 contact: {
56 address: string;
57 visitDate?: Date;
58 }[];
59}
60interface IPerson {
61 contact: { address: string; } & { visitDate: Date; }
62}
63type TPerson = {
64 [x: `contact.address`]: string;
65 [x: `contact.visitDate`]?: Date;
66 contact: { address: string; } & { visitDate: Date; }
67}
68interface IPerson {
69 birth: Date;
70}
71type TPerson = {
72 birth: Date;
73}
74type TPerson = {
75 age: Date;
76 "age.toDateString": () => string;
77 "age.toTimeString": () => string;
78 "age.toLocaleDateString": {
79 ...
80}
81type Structure = {
82 user: {
83 name: string,
84 surname: string
85 }
86}
87
88type BlackMagic<T>= T
89
90// user.name | user.surname
91type Result=BlackMagic<Structure>
92type Structure = {
93 user: {
94 arr: [1, 2],
95 }
96}
97
98type BlackMagic<T> = T
99
100// "user.arr" | "user.arr.0" | "user.arr.1"
101type Result = BlackMagic<Structure>
102type Structure = {
103 user: {
104 arr: number[],
105 }
106}
107
108type BlackMagic<T> = T
109
110// "user.arr" | `user.arr.${number}`
111type Result = BlackMagic<Structure>
112type Structure = {
113 user: {
114 arr: [],
115 }
116}
117
118type BlackMagic<T> = T
119
120// "user.arr" | "user.arr.-1"
121type Result = BlackMagic<Structure>
122type Values<T> = T[keyof T]
123{
124 // 1 | "John"
125 type _ = Values<{ age: 1, name: 'John' }>
126}
127
128type IsNever<T> = [T] extends [never] ? true : false;
129{
130 type _ = IsNever<never> // true
131 type __ = IsNever<true> // false
132}
133
134type IsTuple<T> =
135 (T extends Array<any> ?
136 (T['length'] extends number
137 ? (number extends T['length']
138 ? false
139 : true)
140 : true)
141 : false)
142{
143 type _ = IsTuple<[1, 2]> // true
144 type __ = IsTuple<number[]> // false
145 type ___ = IsTuple<{ length: 2 }> // false
146}
147
148type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
149{
150 type _ = IsEmptyTuple<[]> // true
151 type __ = IsEmptyTuple<[1]> // false
152 type ___ = IsEmptyTuple<number[]> // false
153
154}
155/**
156 * If Cache is empty return Prop without dot,
157 * to avoid ".user"
158 */
159type HandleDot<
160 Cache extends string,
161 Prop extends string | number
162 > =
163 Cache extends ''
164 ? `${Prop}`
165 : `${Cache}.${Prop}`
166
167/**
168 * Simple iteration through object properties
169 */
170type HandleObject<Obj, Cache extends string> = {
171 [Prop in keyof Obj]:
172 // concat previous Cacha and Prop
173 | HandleDot<Cache, Prop & string>
174 // with next Cache and Prop
175 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
176}[keyof Obj]
177
178type Path<Obj, Cache extends string = ''> =
179 // if Obj is primitive
180 (Obj extends PropertyKey
181 // return Cache
182 ? Cache
183 // if Obj is Array (can be array, tuple, empty tuple)
184 : (Obj extends Array<unknown>
185 // and is tuple
186 ? (IsTuple<Obj> extends true
187 // and tuple is empty
188 ? (IsEmptyTuple<Obj> extends true
189 // call recursively Path with `-1` as an allowed index
190 ? Path<PropertyKey, HandleDot<Cache, -1>>
191 // if tuple is not empty we can handle it as regular object
192 : HandleObject<Obj, Cache>)
193 // if Obj is regular array call Path with union of all elements
194 : Path<Obj[number], HandleDot<Cache, number>>)
195 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
196 : HandleObject<Obj, Cache>)
197 )
198
199// "user" | "user.arr" | `user.arr.${number}`
200type Test = Extract<Path<Structure>, string>
201type WithDot<T extends string> = T extends `${string}.${string}` ? T : never
202type KeysUnion<T, Cache extends string = '', Level extends any[] = []> =
203 T extends PropertyKey ? Cache : {
204 [P in keyof T]:
205 P extends string
206 ? Cache extends ''
207 ? KeysUnion<T[P], `${P}`, [...Level, 1]>
208 : Level['length'] extends 1 // if it is a higher level - proceed
209 ? KeysUnion<T[P], `${Cache}.${P}`, [...Level, 1]>
210 : Level['length'] extends 2 // stop on second level
211 ? Cache | KeysUnion<T[P], `${Cache}`, [...Level, 1]>
212 : never
213 : never
214 }[keyof T]
215
216type Acc = Record<string, any>
217
218type ReducerCallback<Accumulator extends Acc, El extends string> =
219 El extends keyof Accumulator ? Accumulator[El] : Accumulator
220
221type Reducer<
222 Keys extends string,
223 Accumulator extends Acc = {}
224 > =
225 // Key destructure
226 Keys extends `${infer Prop}.${infer Rest}`
227 // call Reducer with callback, just like in JS
228 ? Reducer<Rest, ReducerCallback<Accumulator, Prop>>
229 // this is the last part of path because no dot
230 : Keys extends `${infer Last}`
231 // call reducer with last part
232 ? ReducerCallback<Accumulator, Last>
233 : never
234
235{
236 type _ = Reducer<'user.arr', Structure> // []
237 type __ = Reducer<'user', Structure> // { arr: [] }
238}
239type Structure = {
240 user: {
241 tuple: [42],
242 emptyTuple: [],
243 array: { age: number }[]
244 }
245}
246
247
248type Values<T> = T[keyof T]
249{
250 // 1 | "John"
251 type _ = Values<{ age: 1, name: 'John' }>
252}
253
254type IsNever<T> = [T] extends [never] ? true : false;
255{
256 type _ = IsNever<never> // true
257 type __ = IsNever<true> // false
258}
259
260type IsTuple<T> =
261 (T extends Array<any> ?
262 (T['length'] extends number
263 ? (number extends T['length']
264 ? false
265 : true)
266 : true)
267 : false)
268{
269 type _ = IsTuple<[1, 2]> // true
270 type __ = IsTuple<number[]> // false
271 type ___ = IsTuple<{ length: 2 }> // false
272}
273
274type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
275{
276 type _ = IsEmptyTuple<[]> // true
277 type __ = IsEmptyTuple<[1]> // false
278 type ___ = IsEmptyTuple<number[]> // false
279}
280
281/**
282 * If Cache is empty return Prop without dot,
283 * to avoid ".user"
284 */
285type HandleDot<
286 Cache extends string,
287 Prop extends string | number
288 > =
289 Cache extends ''
290 ? `${Prop}`
291 : `${Cache}.${Prop}`
292
293/**
294 * Simple iteration through object properties
295 */
296type HandleObject<Obj, Cache extends string> = {
297 [Prop in keyof Obj]:
298 // concat previous Cacha and Prop
299 | HandleDot<Cache, Prop & string>
300 // with next Cache and Prop
301 | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
302}[keyof Obj]
303
304type Path<Obj, Cache extends string = ''> =
305 (Obj extends PropertyKey
306 // return Cache
307 ? Cache
308 // if Obj is Array (can be array, tuple, empty tuple)
309 : (Obj extends Array<unknown>
310 // and is tuple
311 ? (IsTuple<Obj> extends true
312 // and tuple is empty
313 ? (IsEmptyTuple<Obj> extends true
314 // call recursively Path with `-1` as an allowed index
315 ? Path<PropertyKey, HandleDot<Cache, -1>>
316 // if tuple is not empty we can handle it as regular object
317 : HandleObject<Obj, Cache>)
318 // if Obj is regular array call Path with union of all elements
319 : Path<Obj[number], HandleDot<Cache, number>>)
320 // if Obj is neither Array nor Tuple nor Primitive - treat is as object
321 : HandleObject<Obj, Cache>)
322 )
323
324type WithDot<T extends string> = T extends `${string}.${string}` ? T : never
325
326
327// "user" | "user.arr" | `user.arr.${number}`
328type Test = WithDot<Extract<Path<Structure>, string>>
329
330
331
332type Acc = Record<string, any>
333
334type ReducerCallback<Accumulator extends Acc, El extends string> =
335 El extends keyof Accumulator ? Accumulator[El] : El extends '-1' ? never : Accumulator
336
337type Reducer<
338 Keys extends string,
339 Accumulator extends Acc = {}
340 > =
341 // Key destructure
342 Keys extends `${infer Prop}.${infer Rest}`
343 // call Reducer with callback, just like in JS
344 ? Reducer<Rest, ReducerCallback<Accumulator, Prop>>
345 // this is the last part of path because no dot
346 : Keys extends `${infer Last}`
347 // call reducer with last part
348 ? ReducerCallback<Accumulator, Last>
349 : never
350
351{
352 type _ = Reducer<'user.arr', Structure> // []
353 type __ = Reducer<'user', Structure> // { arr: [] }
354}
355
356type BlackMagic<T> = T & {
357 [Prop in WithDot<Extract<Path<T>, string>>]: Reducer<Prop, T>
358}
359
360type Result = BlackMagic<Structure>
361
This implementation is worth considering
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in REST
Tutorials and Learning Resources are not available at this moment for REST